Description
N个数排成一个环,请选出不超过K段的连续的数,段与段间不能重叠,且
使得选出的数和最大。
Input
输入文件sum.in第一行包含两个正整数N和k。
接下来1行描述这N个数。
Output
输出文件sum.out包含一个数,即要去的最大的和。
Sample Input
9 2
2 -1 2 -1 2 -4 1 -1 2
Sample Output
7
Data Constraint
Hint
20%:K=1,N<=1000
另外20%:K=1
另外20%:N<=1000
100%:K<=10,N<=100000
Source / Author: NOIP2012模拟10.9
题解:
设f[i][j][k][l]表示到i点选了j段了,k表示i点是否选择,l表示1点是否被选。
f[i][j][0] = max( f[i-1][j][0] , f[i-1][j][1]) + 0;
f[i][j][1] = max(f[i-1][j][1] , f[i-1][j-1][0]) + a[i];
至于f的最后一维,只能从上一位同样的状态得来(f[ ][ ][ ][1] = max(f[ ][ ][ ][ ][1] ,f[ ][ ][ ][ 1]) ; f[ ][ ][ ][0] = max(f[ ][ ][ ][ ][0] ,f[ ][ ][ ][ 0]) )
因此,要预处理f[1][][][]
#include<bits/stdc++.h>
#define N 100010
#define K 20
#define ll long long
#define inf 2147483647
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
ll n,k,i,j,l,ans,tmp;
ll f[N][K][2][2],a[N],sum[N],maxx[N];
int main()
{
open("circle");
scanf("%lld%lld",&n,&k);
for(i=1;i<=n;i++)
scanf("%lld",&a[i]);
memset(f,128,sizeof(f));
f[1][0][0][0]=0;
f[1][1][1][1]=a[1];
for(i=n;i>0;i--)sum[i] = sum[i+1] + a[i];
for(i=n;i>0;i--)maxx[i] = max(sum[i] , maxx[i+1]);
for(i=2;i<=n;i++)
{
// for(j=1;j<=k+1;j++)
for(j=1;j<=k;j++)
{
for(l=0;l<2;l++)
{
if(f[i-1][j][0][l]>=0 && f[i-1][j][1][l]>=0)f[i][j][0][l] = max(f[i-1][j][0][l] , f[i-1][j][1][l]);
else if(f[i-1][j][0][l]>=0)f[i][j][0][l] = f[i-1][j][0][l]; else if (f[i-1][j][1][l]>=0) f[i][j][0][l]=f[i-1][j][1][l];
if(f[i-1][j][1][l]>=0 && f[i-1][j-1][0][l]>=0)f[i][j][1][l] = max(f[i-1][j][1][l] , f[i-1][j-1][0][l]) + a[i];
else if(f[i-1][j][1][l]>=0)f[i][j][1][l] = f[i-1][j][1][l]+a[i]; else if(f[i-1][j-1][0][l]>=0)f[i][j][1][l] = f[i-1][j-1][0][l] + a[i];
// if(j!=k+1)
if(l==1) tmp= maxx[i+1]; else tmp=0;
ans = max(ans , max(f[i][j][0][l],f[i][j][1][l])+tmp);
}
}
}
// ans = max(ans , f[n][k+1][1][1]);
printf("%lld",ans);
return 0;
}