naptime

naptime

有一个长度为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\n",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();
}

posted @ 2019-05-27 16:52  a1b3c7d9  阅读(257)  评论(0编辑  收藏  举报