HDU1024 Max Sum Plus Plus


Max Sum Plus Plus

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 24256    Accepted Submission(s): 8313


Problem Description
Now I think you have got an AC in Ignatius.L's "Max Sum" problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem.

Given a consecutive number sequence S 1, S 2, S 3, S 4 ... S x, ... S n (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ S x ≤ 32767). We define a function sum(i, j) = S i + ... + S j (1 ≤ i ≤ j ≤ n).

Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i 1, j 1) + sum(i 2, j 2) + sum(i 3, j 3) + ... + sum(i m, j m) maximal (i x ≤ i y ≤ j x or i x ≤ j y≤ j x is not allowed).

But I`m lazy, I don't want to write a special-judge module, so you don't have to output m pairs of i and j, just output the maximal summation of sum(i x, j x)(1 ≤ x ≤ m) instead. ^_^
 

Input
Each test case will begin with two integers m and n, followed by n integers S 1, S 2, S 3 ... S n.
Process to the end of file.
 

Output
Output the maximal summation described above in one line.
 

Sample Input
  
  
1 3 1 2 3 2 6 -1 4 -2 3 -2 3
 

Sample Output
  
  
6 8
Hint
Huge input, scanf and dynamic programming is recommended.
 

Author
JGShining(极光炫影)
 

题意:给定n个数,求不相交的m段连续子序列最大和。

思路:用dp[i][j]表示将j个数字分成i段,并且最后一段以numb[j]结尾的最大和,那么dp[i][j]的来源情况只有两种:

1.将前j-1个数分成i段,然后把这个数直接接在最后一段,那么dp[i][j] = dp[i][j-1] + numb[j].

2.将前j-1个数分成i-1段,然后再将第j个数变成独立的一段 那么dp[i][j] = max(dp[i-1][k])(i-1<=k<j)

综上可以得到转移方程:dp[i][j] = max( dp[i][j-1] + numb[j],  max(dp[i-1][k])(i-1<=k<j) )

但是n的范围是1e6,显然开二维数组是不行的,需要优化一下。

优化:发现dp[i][j] 之和dp[i][j-1]以及dp[i-1][k]有关,我们可以用一个一维数组,每次滚动记录dp[i][j].

但是这样的话,寻找max(dp[i-1][k])还需要遍历一遍,显然是不合理的,所以我们再开一个数组pre[j]记录max(dp[i-1][k])(i-1<=k<j).

这样我们便可以每次直接读取这个值即可。

#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <string>
#if defined(_MSC_VER) || __cplusplus > 199711L
#define aut(r,v) auto r = (v)
#else
#define aut(r,v) __typeof(v) r = (v)
#endif
#define each(it,o) for(aut(it, (o).begin()); it != (o).end(); ++ it)
#define fur(i,a,b) for(int i=(a);i<=(b);i++)
#define furr(i,a,b) for(int i=(a);i>=(b);i--)
#define cl(a) memset((a),0,sizeof(a))
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define sc(x) scanf("%d",&x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int, int> pii;
const int inf=0x3f3f3f3f;
const double eps=1e-8;
const int mod=1000000007;
const double pi=acos(-1);
inline void gn(long long&x){
    int sg=1;char c;while(((c=getchar())<'0'||c>'9')&&c!='-');c=='-'?(sg=-1,x=0):(x=c-'0');
    while((c=getchar())>='0'&&c<='9')x=x*10+c-'0';x*=sg;
}
inline void gn(int&x){long long t;gn(t);x=t;}
inline void gn(unsigned long long&x){long long t;gn(t);x=t;}
inline void gn(double&x){double t;scanf("%lf",&t);x=t;}
inline void gn(long double&x){double t;scanf("%lf",&t);x=t;}
int gcd(int a,int b){return a? gcd(b%a,a):b;}
ll powmod(ll a,ll x,ll mod){ll t=1ll;while(x){if(x&1)t=t*a%mod;a=a*a%mod;x>>=1;}return t;}
// (づ°ω°)づe★
int numb[1000005];
int dp[1000005];
int pre[1000005];
void init(){
    cl(dp);
    cl(pre);
}
int n,m; 
int main(){
    int tmp;
    while(~scanf("%d %d",&m,&n)){
        init();
        fur(i,1,n)gn(numb[i]);
        fur(i,1,m){ 
            tmp = -inf;     
            fur(j,i,n){        
                dp[j] = max(dp[j-1],pre[j-1]) + numb[j];
                pre[j-1] = tmp;         //因为计算到dp[j]的时候 ,需要用到pre[j-1] 
                tmp = max(dp[j],tmp);    //所以我们不能在获得dp[j-1]的时候立刻更新pre[j-1]
            }                            //而是先临时保存一下这个值
        }                                //当计算完dp[j]以后再修改dp[j-1]; 
        printf("%d\n",tmp);            //因为最大的值不一定是以numb[n]结尾的 
    }                                //所以不能输出 dp[n] 而是应该输出dn[i]里面最大的那个 
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值