为了肾六
时间限制:
4000 ms | 内存限制:
210535 KB
难度:2
-
描述
-
最近肾六很流行,goshawk看身边的朋友都用上了apple。自己还用着W年前的Samsung。于是决定去IT公司打工,都是为了肾六。现在上司让他解决下面的一个小问题,但是goshawk没学好算法,被这个问题难住了,聪明的你帮帮他吧。
给一个n个整数的序列p1,p2,p3.....pn。你要以下面的方式选k对整数。[l1, r1], [l2, r2], ..., [lk, rk] (1 ≤ l1 ≤ r1 < l2 ≤ r2 < ... < lk ≤ rk ≤ n;ri - li + 1 = m), 为了让这个表达式的值尽可能大。赶快帮他解决这个问题吧。
-
输入
- 第一行包含三个整数n,m,和k(1 ≤ (m × k) ≤ n ≤ 5000)。第二行包含n个整数p1,p2,p3.....pn(0 ≤ pi ≤ 10^9). 输出
- 单行输出一个整数。最大和的值。 样例输入
-
5 2 1
-
1 2 3 4 5
-
7 1 3
-
2 10 7 18 5 33 0
样例输出
-
9
-
61
-
//唉,写了几个小时了,还是WA,还是太菜了。。。
-
就先占上WA的代码,以后再看。。。
-
#include<stdio.h> #include<string.h> #include<algorithm> #define ll long long #define INF -0x3f3f3f3f using namespace std; int a[5010]; int vis[5010]; int main() { int n,m,k; int i,j,jj,kk,p; while(scanf("%d%d%d",&n,&m,&k)!=EOF) { memset(vis,0,sizeof(vis)); memset(a,0,sizeof(a)); for(i=1;i<=n;i++) scanf("%lld",&a[i]); ll sum=0; ll s,mm; while(k--) { mm=0; for(i=1;i<=n;i++) { kk=0;s=0; if(!vis[i]) { j=i; while(kk<m) { while(a[j]==INF) j++; s+=a[j]; j++;kk++; } } if(s>mm) { mm=s; p=i; jj=j; } } sum+=mm; for(i=p;i<jj;i++) { vis[i]=1; a[i]=INF; } } printf("%lld\n",sum); } }
//唉,看了协会里大神的博客才懂。。。下面的是大神的思路。。(好厉害) -
思路:dp[k][i]表示前i个数取k组得到的最大贡献。
dp[k][i] = max(dp[k][i-1], dp[k-1][i-M]+(a[i-M+1] + ...+a[i]))。
用滚动数组优化下就可以了。
#include<stdio.h> #include<string.h> #include<algorithm> #define ll long long #define N 100010 using namespace std; ll sum[N]; ll p[N]; ll dp[2][N]; int main() { int n,m,k; int i,j,l; while(scanf("%d%d%d",&n,&m,&k)!=EOF) { ll ans=0; for(i=1;i<=n;i++) { scanf("%lld",&p[i]); sum[i]=0; ans+=p[i]; if(i<m) continue; for(j=i;j>i-m;j--) sum[i]+=p[j]; } if(n==k||m==n) { printf("%lld\n",ans); continue; } memset(dp,0,sizeof(dp)); ans=0; for(l=1;l<=k;l++) { for(i=l*m;i<=n;i++) { dp[l&1][i]=max(dp[l&1][i],dp[l&1][i-1]); dp[l&1][i]=max(dp[l&1][i],dp[(l-1)&1][i-m]+sum[i]); ans=max(ans,dp[k&1][i]); } } printf("%lld\n",ans); } return 0; }