题解
20pts
暴力 dfs,判断是否是严格上升子序列,最后求最大值。
dfs 过程中可以记录上一项以及当前的序列和,方便剪枝和求和。
时间复杂度
O
(
2
n
)
\operatorname{O}(2^n)
O(2n)。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x = 0,p = 1;
char c = getchar();
while(c > '9' || c < '0') p = c == '-' ? -1 : 1,c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48),c = getchar();
return p * x;
}
template<typename _Tp> inline void _write(_Tp x) { if(x > 9) _write(x / 10); putchar(x % 10 | 48); }
template<typename _Tp> inline void write(_Tp x) { if(x < 0) putchar('-'),x = -x; _write(x); }
template<typename _Tp> inline void writeln(_Tp x) { write(x); puts(""); }
const int N = 1e5 + 10;
int n,a[N],ans = 0;
void dfs(int step,int last = 0,int sum = 0)
{
if(step == n + 1)
{
ans = max(ans,sum);
return;
}
dfs(step + 1,last,sum);
if(a[step] > last) dfs(step + 1,a[step],sum + a[step]);
}
signed main()
{
n = read();
for(int i = 1;i <= n;i++) a[i] = read();
dfs(1);
writeln(ans);
return 0;
}
40pts
考虑 dp。设
f
i
f_i
fi 为最后一项是
a
i
a_i
ai 时的最优情况。
可以得到状态转移方程:
f
i
=
max
j
=
1
i
−
1
f
j
×
[
a
j
<
a
i
]
+
a
i
f_i=\max_{j=1}^{i-1}{f_j \times [a_j < a_i]}+a_i
fi=maxj=1i−1fj×[aj<ai]+ai。
其中当
a
j
<
a
i
a_j<a_i
aj<ai时
[
a
j
<
a
i
]
[a_j<a_i]
[aj<ai] 为
1
1
1,否则值为
0
0
0。
最初
f
1
=
a
1
f_1=a_1
f1=a1,最终答案为
max
i
=
1
n
f
i
\max_{i=1}^nf_i
maxi=1nfi。
时间复杂度
O
(
n
2
)
\operatorname{O}(n^2)
O(n2)。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x = 0,p = 1;
char c = getchar();
while(c > '9' || c < '0') p = c == '-' ? -1 : 1,c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48),c = getchar();
return p * x;
}
template<typename _Tp> inline void _write(_Tp x) { if(x > 9) _write(x / 10); putchar(x % 10 | 48); }
template<typename _Tp> inline void write(_Tp x) { if(x < 0) putchar('-'),x = -x; _write(x); }
template<typename _Tp> inline void writeln(_Tp x) { write(x); puts(""); }
const int N = 1e5 + 10;
int n,a[N],f[N];
signed main()
{
n = read();
for(int i = 1;i <= n;i++) a[i] = read();
f[1] = a[1];
for(int i = 2;i <= n;i++)
for(int j = 1;j < i;j++)
if(a[j] < a[i])
f[i] = max(f[i],f[j] + a[i]);
writeln(*max_element(f + 1,f + n + 1));
return 0;
}
70pts
换一种 dp 方式,设
f
i
,
j
f_{i,j}
fi,j 为在前
i
i
i 个数中,以
j
j
j 结尾的子序列的最值,其中
j
≤
1
0
3
j \le 10^3
j≤103。
所以
f
i
,
j
=
max
k
=
1
i
−
1
max
l
=
1
j
−
1
f
k
,
l
+
a
i
f_{i,j} = \max_{k=1}^{i-1}\max_{l=1}^{j-1}f_{k,l}+a_i
fi,j=maxk=1i−1maxl=1j−1fk,l+ai。
答案为
max
i
=
1
n
max
j
=
1
m
f
i
,
j
\max_{i=1}^n\max_{j=1}^mf_{i,j}
maxi=1nmaxj=1mfi,j。
暴力 dp 时间复杂度为
O
(
m
n
)
\operatorname{O}(mn)
O(mn),其中
m
m
m 为
a
i
a_i
ai 的值域。但是此时空间复杂度太高,于是需要降维。
降维后可以发现转移方程变为
f
j
=
max
k
=
1
j
−
1
f
k
+
j
f_j = \max_{k=1}^{j-1}f_k+j
fj=maxk=1j−1fk+j。
其中求
max
k
=
1
j
−
1
\max_{k=1}^{j-1}
maxk=1j−1 时可以使用树状数组将时间复杂度降至
O
(
log
m
)
\operatorname{O}(\log m)
O(logm),空间复杂度
O
(
m
)
\operatorname{O}(m)
O(m)。
总时间复杂度
O
(
n
log
m
)
\operatorname{O}(n \log m)
O(nlogm)。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
int read()
{
int x = 0,p = 1;
char c = getchar();
while(c > '9' || c < '0') p = c == '-' ? -1 : 1,c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48),c = getchar();
return p * x;
}
void _write(int x) { if(x > 9) _write(x / 10); putchar(x % 10 | 48); }
void write(int x) { if(x < 0) putchar('-'),x = -x; _write(x); }
void writeln(int x) { write(x); puts(""); }
const int N = 1e5 + 10;
int n,a[N];
int bit[1010];
struct BIT
{
int N;
BIT() : N(1000) {}
int f(int x) { return x & -x; }
void add(int x,int k)
{
while(x <= N)
{
bit[x] = max(bit[x],k);
x += f(x);
}
}
int _max(int n)
{
int res = 0;
while(n >= 1)
{
res = max(res,bit[n]);
n -= f(n);
}
return res;
}
};
signed main()
{
n = read();
for(int i = 1;i <= n;i++) a[i] = read();
BIT _bit;
for(int i = 1;i <= n;i++)
{
_bit.add(a[i],a[i] + _bit._max(a[i] - 1));
}
cout << _bit._max(1000) << endl;
return 0;
}
100pts
在 70pts 的基础上进行离散化。
时间复杂度
O
(
n
log
n
)
\operatorname{O}(n \log n)
O(nlogn)。
空间复杂度
O
(
n
)
\operatorname{O}(n)
O(n)。
AC Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
int read()
{
int x = 0,p = 1;
char c = getchar();
while(c > '9' || c < '0') p = c == '-' ? -1 : 1,c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48),c = getchar();
return p * x;
}
void _write(int x) { if(x > 9) _write(x / 10); putchar(x % 10 | 48); }
void write(int x) { if(x < 0) putchar('-'),x = -x; _write(x); }
void writeln(int x) { write(x); puts(""); }
const int N = 1e5 + 10;
int n,a[N],b[N];
map<int,int> rk;
int bit[N];
struct BIT
{
int f(int x) { return x & -x; }
void add(int x,int k)
{
while(x <= n)
{
bit[x] = max(bit[x],k);
x += f(x);
}
}
int _max(int n)
{
int res = 0;
while(n >= 1)
{
res = max(res,bit[n]);
n -= f(n);
}
return res;
}
};
signed main()
{
n = read();
for(int i = 1;i <= n;i++) b[i] = a[i] = read();
sort(b + 1,b + n + 1);
for(int i = 1;i <= n;i++) rk[b[i]] = i;
BIT _bit;
for(int i = 1;i <= n;i++)
{
_bit.add(rk[a[i]],a[i] + _bit._max(rk[a[i]] - 1));
}
cout << _bit._max(n) << endl;
return 0;
}