51nod 1052 最大M子段和

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 }
原文地址:https://www.cnblogs.com/zyb993963526/p/7616888.html