题目:POJ2888.
题目大意:给定一个长度为
n
n
n的环状数列,在这个环状数列上取
m
m
m个数,其中每一个极大连续段的开头没有贡献,其余均有贡献,求最大贡献和.
0
≤
b
≤
n
≤
3600
0\leq b\leq n\leq 3600
0≤b≤n≤3600.
首先不考虑这是个环而是个序列,直接DP.设
f
[
i
]
[
j
]
[
0
/
1
]
f[i][j][0/1]
f[i][j][0/1]表示第
1
1
1到
i
i
i个数中取了
j
j
j个数且第
i
i
i个数不选
/
/
/选的最大贡献和,容易列出方程:
f
[
i
]
[
j
]
[
0
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
[
0
]
,
f
[
i
−
1
]
[
j
]
[
1
]
)
f
[
i
]
[
j
]
[
1
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
1
]
[
0
]
,
f
[
i
−
1
]
[
j
−
1
]
[
1
]
+
a
[
i
]
)
.
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1])\\ f[i][j][1]=max(f[i-1][j-1][0],f[i-1][j-1][1]+a[i]).
f[i][j][0]=max(f[i−1][j][0],f[i−1][j][1])f[i][j][1]=max(f[i−1][j−1][0],f[i−1][j−1][1]+a[i]).
初态 f [ 1 ] [ 0 ] [ 0 ] = f [ 1 ] [ 1 ] [ 1 ] = 0 f[1][0][0]=f[1][1][1]=0 f[1][0][0]=f[1][1][1]=0,答案 m a x ( f [ n ] [ m ] [ 0 ] , f [ n ] [ m ] [ 1 ] ) max(f[n][m][0],f[n][m][1]) max(f[n][m][0],f[n][m][1]).显然这个DP可以做到 O ( n 2 ) O(n^2) O(n2).
考虑如何把这个做法拓展到环上.
重新考虑一下我们漏掉了哪种情况,发现只漏掉了一种第 n n n个和第 1 1 1个同时选的情况,这个情况下貌似只需要把初态改成 f [ 1 ] [ 1 ] [ 1 ] = a [ 1 ] f[1][1][1]=a[1] f[1][1][1]=a[1],答案变为 f [ n ] [ m ] [ 1 ] f[n][m][1] f[n][m][1]即可.
于是我们只需要跑两遍DP即可解决这个问题,时空复杂度 O ( n 2 ) O(n^2) O(n2).
然而这个做法在POJ上会爆空间,所以需要滚动数组优化一下空间复杂度.
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=3830,INF=(1<<30)-1;
int n,m,a[N+9],ans,dp[N+9][2];
void Pre_dp(){
for (int j=0;j<=m;++j)
for (int k=0;k<2;++k)
dp[j][k]=-INF;
}
void Solve_dp(){
for (int i=2;i<=n;++i)
for (int j=m;j>=0;--j){
dp[j][0]=max(dp[j][0],dp[j][1]);
if (j>0) dp[j][1]=max(dp[j-1][0],dp[j-1][1]+a[i]);
}
}
Abigail into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
}
Abigail work(){
if (n<2) return;
Pre_dp();
dp[0][0]=dp[1][1]=0;
Solve_dp();
ans=max(dp[m][0],dp[m][1]);
Pre_dp();
dp[1][1]=a[1];
Solve_dp();
ans=max(ans,dp[m][1]);
}
Abigail outo(){
printf("%d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}