http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1052
题意:
思路:
设$dp[i][j]$表示前j个数构成i个字段时的最大值,并且必须以j结尾。
那么状态转移方程就是:
①$dp[i][j]=max(dp[i][j],dp[i][j-1]+a[j])$,此时是将j接在第i个字段的末尾,字段数不增加。
②$dp[i][j]=max(dp[i][j],dp[i-1][t],+a[j])$ $(i-1<=t<j)$,此时是让j单独成为一个字段的段首。
对于第二种情况的话,每次去枚举t的话会比较耗时,其实我们只需要记录好上一行的状态,然后每次取最大值即可,也就是用滚动数组来实现。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 #include<stack> 7 #include<queue> 8 #include<cmath> 9 #include<map> 10 #include<set> 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,int> pll; 14 const int INF = 0x3f3f3f3f; 15 const int maxn=5000+5; 16 17 int n, m; 18 int a[maxn]; 19 ll dp[maxn][2]; 20 21 int main() 22 { 23 //freopen("in.txt","r",stdin); 24 while(~scanf("%d%d",&n,&m)) 25 { 26 ll sum=0; 27 int cnt=0; 28 for(int i=1;i<=n;i++) 29 { 30 scanf("%d",&a[i]); 31 if(a[i]>0) {cnt++;sum+=a[i];} 32 } 33 int cur=0; 34 if(m>=cnt) printf("%lld ",sum); 35 else 36 { 37 38 for(int i=1;i<=m;i++) 39 { 40 cur^=1; 41 dp[i][cur]=dp[i][cur^1]+a[i]; //先赋初值,将前面的i个数每个数都分成1段 42 ll MAX=dp[i-1][cur^1]; //取上一行前i-1个数的最大值 43 for(int j=i;j<=n-m+i;j++) 44 { 45 dp[j][cur]=max(dp[j-1][cur],MAX)+a[j]; //状态转移方程的选择 46 if(MAX<dp[j][cur^1]) MAX=dp[j][cur^1]; //动态维护上一行前j个的最大值 47 } 48 } 49 ll ans=0; 50 for(int i=1;i<=n;i++) ans=max(ans,dp[i][cur]); 51 printf("%lld ",ans); 52 } 53 54 } 55 return 0; 56 }