题意:
将
n
n
n个数划分成不超过
m
m
m个不相交的子段,问子段和最大可以为多少?
思路:
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为前
i
i
i个数划分成不超过
j
j
j个不相交子段的最大和。
显然有:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
m
a
x
(
d
p
[
k
−
1
]
[
j
−
1
]
+
s
u
m
[
i
]
−
s
u
m
[
k
−
1
]
)
(
1
<
=
k
<
=
i
)
)
dp[i][j] = max(dp[i-1][j],max(dp[k-1][j-1] + sum[i] - sum[k-1]) ( 1 <=k <= i) )
dp[i][j]=max(dp[i−1][j],max(dp[k−1][j−1]+sum[i]−sum[k−1])(1<=k<=i))
很明显这是一个 O ( n 2 m ) O(n^2m) O(n2m)的复杂度,不可承受。
考虑优化。
之前一直在想数据结构方面的优化,但一直没有什么进展。
故考虑内部
k
k
k的循环是否也可以用DP来维护。
设
g
[
i
]
[
j
]
=
m
a
x
(
d
p
[
k
−
1
]
[
j
−
1
]
−
s
u
m
[
k
−
1
]
)
(
1
<
=
k
<
=
i
)
g[i][j] = max(dp[k-1][j-1] - sum[k-1]) (1<=k<=i)
g[i][j]=max(dp[k−1][j−1]−sum[k−1])(1<=k<=i)
观察易得:
g
[
i
]
[
j
]
=
m
a
x
(
g
[
i
−
1
]
[
j
]
,
d
p
[
i
−
1
]
[
j
−
1
]
−
s
u
m
[
i
−
1
]
)
g[i][j] = max(g[i-1][j],dp[i-1][j-1] - sum[i-1])
g[i][j]=max(g[i−1][j],dp[i−1][j−1]−sum[i−1])
故最终有:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
g
[
i
]
[
j
]
+
s
u
m
[
i
]
)
dp[i][j] = max(dp[i-1][j],g[i][j] + sum[i])
dp[i][j]=max(dp[i−1][j],g[i][j]+sum[i])
最后可以用滚动数组优化内存。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int A = 5000 + 10;
ll dp[2][A],g[2][A],sum[A];
int main(){
//freopen("output","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
sum[0] = 0;
for(int i=1 ;i<=n ;i++){
ll x;
scanf("%I64d",&x);
sum[i] = sum[i-1] + x;
}
for(int i=1 ;i<=n ;i++){
for(int j=1 ;j<=m ;j++){
g[i&1][j] = max(g[(i-1)&1][j],dp[(i-1)&1][j-1] - sum[i-1]);
dp[i&1][j] = max(dp[i&1][j],dp[(i-1)&1][j]);
dp[i&1][j] = max(dp[i&1][j],g[i&1][j] + sum[i]);
}
}
printf("%I64d\n",dp[n&1][m]);
return 0;
}