这个题个人认为是我目前所做的最难的区间dp了,以前把环变成链的方法在这个题上并不能使用,因为那样可能存在重复计算
我第一遍想的时候就是直接把环变成链了,wa了5个点,然后仔细思考一下就发现了问题
比如这个样例
5 4
1 2 4 1 1
这个样例变成链以后就是这样
1 2 4 1 1 1 2 4 1 1
在这个链上,很明显,取[2,3]和[7,8]是最优方案,答案是8,但是很容易可以算出,这个样例真正答案是7,2+4+1。
因此这个方法是不可行的,我在查阅了网上唯一的题解发现,这个问题可以灰常巧妙的用初始赋值来解决,我们可以进行两遍递推,第一遍认为第一个点不可以被加进答案,第二遍认为第二个点可以被加进答案
第一遍非常好理解,是基于不考虑时间成环的,第二个有点难想,假如第一个点能选,则说明至少在原序列终点及以前以前有个点是卖萌开始的起始点
那么我们处理的时候,会认为在原序列上,从真实的起始点一直到最后原序列终点被选了
比如这个样例
5 3
5 1 1 1 3
1 0 0 1 1 0为未选择,1为选择
答案是8,第二遍递推会认为1-1,4-5这两个区间合起来是答案,就是这样来保证正确性的。
#include<iostream> #include<cstdio> using namespace std; int n,m,dp[2][3610][2],qwq[7210],ans,t; inline void init() { for(int j=0;j<=m;j++) dp[0][j][0]=dp[0][j][1]=dp[1][j][0]=dp[1][j][1]=-1e9; } void dp1() { t=0; for(int i=2;i<=n;i++) { t=1-t; for(int j=0;j<=m;j++) { dp[t][j][0]=max(dp[1-t][j][0],dp[1-t][j][1]); if(j>0) dp[t][j][1]=max(dp[1-t][j-1][0],dp[1-t][j-1][1]+qwq[i]); } } } int main() { scanf("%d%d",&n,&m); if(m<=1)//特判一下,如果卖萌时间就给了1或0,答案一定为0 { printf("0");return 0; } for(int i=1;i<=n;i++) scanf("%d",&qwq[i]); init();//预处理,需要用滚动数组优化时间 dp[0][1][1]=dp[0][0][0]=0;//当没有从最后一个位置开始的时候,第一个时段答案肯定为0 dp1(); ans=max(dp[t][m][0],dp[t][m][1]);//在最后时段取与不取之间找个最大值 init(); dp[0][1][0]=dp[0][1][1]=qwq[1];//当从最后一个时段开始的时候 dp1();//进行两遍递推 ans=max(ans,dp[t][m][1]); printf("%d",ans); }