现在的位置: 首页 > 综合 > 正文

hdu1024求多段最大子序列和

2014年01月07日 ⁄ 综合 ⁄ 共 3248字 ⁄ 字号 评论关闭

发现自己变懒了,不想详细写思路了

简单思路:计算n个数m段的子序列最大和,即计算前n个数m段的最大子序列和,前n个数,m段,马上想到这是动态规划,假设prior[n][m]代表前n个数m段子序列最大和,现在我们已经选出来了m段,可以考虑前n个数这m段包括第n个数在内吗??分两种情况:包括与不包括,不包括就是第n个数没用了,也就就转化为前n-1个数m段最大子序列和prior[n-1][m],如果包括则假设前n个数m段且包括第n个数为:present[n][m];

所以prior[n][m]=max(prior[n-1][m],present[n][m]);

同时易知present[n][m]=max(present[n-1][m]+s[n],prior[n-1][m-1]+s[n]);

把n,m换成j,i就得到了状态转移方程:present[j][i]=max(present[j-1][i]+s[j],prior[j-1][j-1]+s[j]);

                                                          prior[j][i]=max(prior[j-1][i],present[j][i]);

于是有下面初步代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;

const int MAX=100;
//分别代表前i个数j段且包括第i个数的最大值和前i个数j段的最大值 
int present[MAX][MAX],prior[MAX][MAX];
int s[MAX];

int main(){
	int m,n;
	while(cin>>m>>n){
		for(int i=1;i<=n;++i){
			for(int j=1;j<=m;++j){
				present[i][j]=prior[i][j]=-INF;
			}
		}
		for(int i=1;i<=n;++i)cin>>s[i];
		for(int i=1;i<=m;++i){
			for(int j=i;j<=n;++j){
				present[j][i]=max(present[j-1][i]+s[j],prior[j-1][i-1]+s[j]);//包括第j个数的前j个数i段的最大值 
				prior[j][i]=max(prior[j-1][i],present[j][i]);//前j个数i段的最大值:不包括第j个数即前j-1个数i段最大值或者包括了第j个数即前j个数i段的最大值 
			}
		}
		cout<<prior[n][m]<<endl;
	}
	return 0;
}

由于本题n,m很大所以不能使用二维数组,会超内存,观察那个动态转移方程可以发现计算i,j时只需要用到j-1的情况,所以考虑省掉第二维数组且采用滚动数组:

present[j]=max(present[j-1]+s[j],prior[j-1]+s[j]);prior[j-1]=max(present[j-1],prior[j-2]);

prior[n]=max(present[n],prior[n-1]);

这里稍微改变下引入sum来存最大值,至于为什么在写代码中会明白,这里就不多说了。

下面是正确代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;

const int MAX=1000001;
int *s,*present,*prior;
//present代表前j个数且包括第j个数的i段子序列最大和,prior表示前j个数i段子序列的最大和 

int main(){
	int m,n;
	while(cin>>m>>n){
		s=new int[n+1];
		for(int i=1;i<=n;++i)scanf("%d",s+i);
		present=new int[n+1];
		prior=(int *)calloc(n+1,sizeof(int));//calloc和maccoc区别在于它分配的内存连续且已对数据初始化为0
		present[0]=0;//只需要对present[0]初始化,因为present[n]可以由present[n-1]得到
		int sum=0;
		for(int i=1;i<=m;++i){
			sum=-INF;
			for(int j=i;j<=n-m+i;++j){//m段时对应最少计算前n个数,m-1段时对应最少要计算前n-1个数,...i段对应最少要计算前n-m+i个数 
				//present[j-1]在这里代表前j-1个数i段序列的最大值,当j=j-1时就已经计算了present[j-1] 
			   //prior[j-1]在这里代表前j-1个数i-1段序列的最大值,因为还没更新prior[j-1]为i段的最大值
				present[j]=max(present[j-1]+s[j],prior[j-1]+s[j]);//将第j个数连载前i段的最后面或者自成一段与前i-1段合成i段 
				prior[j-1]=sum;//更新为前j-1个数i段的最大值,sum就是前j-1个数i段的最大值 
				sum=max(present[j],sum);//计算前j个数i段的最大值 
			}//present[j]=max(present[j-1]+s[j],prior[j-1]+s[j]);prior[j-1]=(present[j-1],prior[j-2]);
			prior[n-m+i]=sum;//prior[n]=max(present[n],prior[n-1]);
		} 
		cout<<sum<<endl;
		delete[] s;
		delete[] present;
		delete[] prior;
	}
	return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=1024

注释之后排版有点乱,下面是未注释的:

#include<iostream>  
#include<cstdio>  
#include<cstdlib>  
#include<cstring>  
#include<string>  
#include<queue>  
#include<algorithm>  
#include<map>  
#include<iomanip>  
#define INF 99999999  
using namespace std;  
  
const int MAX=1000001;  
int *s,*present,*prior;   
  
int main(){  
    int m,n;  
    while(cin>>m>>n){  
        s=new int[n+1];  
        for(int i=1;i<=n;++i)scanf("%d",s+i);  
        present=new int[n+1];  
        prior=(int *)calloc(n+1,sizeof(int));  
        present[0]=0;
        int sum=0;  
        for(int i=1;i<=m;++i){  
            sum=-INF;  
            for(int j=i;j<=n-m+i;++j){  
                present[j]=max(present[j-1]+s[j],prior[j-1]+s[j]);  
                prior[j-1]=sum;   
                sum=max(present[j],sum);  
            }
            prior[n-m+i]=sum; 
        }   
        cout<<sum<<endl;  
        delete[] s;  
        delete[] present;  
        delete[] prior;  
    }  
    return 0;  
}

抱歉!评论已关闭.