有一个长度为n的序列(a_i),首尾相接组成了一个环,现在要在这个环上选出若干个区间,使区间长度之和恰好为b,然后忽略区间的顺时针开头元素,权值累加区间中所有的数字,问权值的最大值,(2 <= b < n <= 3830)。
解
注意到问题是一个环,所以应该拆环成链,而且是区间选择的问题,特别的地方在于,区间选择要忽略开头元素,于是我们需要保存该个元素是否被选,
所以设(f[i][j][0/1])分别表示选到第i个数前面已经选了j个数第i个数选与不选的最大权值,于是不难有
[f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1])
]
[f[i][j][k]=max(f[i-1][j-1][0],f[i-1][j-1][1]+a_i)
]
边界:(f[0][0][0]=0),其余负无限大。
答案:(max(f[n][b][0],f[n][b][1]))
但是注意问题是一个环,而这只是一条链,于是还要联系环,暴力枚举拆哪个位置显然是不行的,但是注意到这个链所没考虑的环的情况,即第一个位置和最后一个位置是否联系起来了,于是我们可以暴力让其联系,把边界改成(f[1][1][1]=a_1),其余负无限大,然后二次递推,答案再考虑一个(f[n][b][1])即可。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define intmax 0x7fffffff
using namespace std;
int u[3831],dp[3831][3831][2];
il void read(int&);
il int max(int,int);
int main(){
int lsy;read(lsy);
while(lsy--){
int n,b,ans;
read(n),read(b),memset(dp,-127,sizeof(dp));
for(int i(1);i<=n;++i)read(u[i]);dp[0][0][0]=0;
for(int i(1),j;i<=n;++i){
dp[i][0][0]=dp[i-1][0][0];
for(j=1;j<=i;++j)
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]),
dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j-1][1]+u[i]);
}ans=max(dp[n][b][0],dp[n][b][1]),memset(dp,-127,sizeof(dp));
dp[1][1][1]=u[1],dp[1][0][0]=0;
for(int i(2),j;i<=n;++i){
dp[i][0][0]=dp[i-1][0][0];
for(j=1;j<=i;++j)
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]),
dp[i][j][1]=max(dp[i-1][j-1][0],dp[i-1][j-1][1]+u[i]);
}ans=max(ans,dp[n][b][1]),printf("%d
",ans);
}
return 0;
}
il int max(int a,int b){
return a>b?a:b;
}
il void read(int &x){
x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}