最大子段和部分问题(DP)

1.一维数组求最大子段和;(HDU1003,HOJ1760)

这就是最基础的最大子段和模型:给出一个序列a[0],a[1],a[2],...a[n],要求出连续的一段,使其总和最大.如果设dp[i]表示以第i个元素为结尾的最大总和,那么显然有:
dp[i] = dp[i - 1] + a[i] (dp[i - 1] > 0 时)
a[i] (dp[i - 1] < 0 时)
显然这个模型空间复杂度可以压缩至O(1),即:
dp += a[i] (dp > 0)
dp = a[i] (dp < 0)

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

//AC(含有最大和的起始位置和终止位置)

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<stdlib.h>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<iomanip>
#include<numeric>
#include <istream>     //基本输入流
#include <ostream>     //基本输出流
#include <sstream>     //基于字符串的流
#include <utility>     //STL 通用模板类
#include <complex.h>   //复数处理
#include <fenv.h>    //浮点环境
#include <inttypes.h>  //整数格式转换
#include <stdbool.h>   //布尔环境
#include <stdint.h>   //整型环境
#include <tgmath.h>   //通用类型数学宏
#define L(a,b,c) for(int a = b;a >= c;a --)
#define M(a,b,c) for(int a = b;a <= c;a ++)
#define N(a,b) memset(a,b,sizeof(a));
const int MAX=1<<30;
const int MIN=-MAX;


using namespace std;
int dp[100011],a[100011];
int main()
{
    int num=0,T,n;
    cin>>T;
    while(T--)
    {
        N(dp,0);
        cin>>n;
        for(int i=1; i<=n; i++)
            cin>>a[i];
        dp[1]=a[1];
        int ans=a[1];   //求和
        int end=1,begin=1,start=1;
        M(i,2,n)
        {
            if(a[i]<=dp[i-1]+a[i])
                dp[i]=dp[i-1]+a[i];
            else        //用start记录此时输入满足题意的第一个位置
                dp[i] = a[i],start=i;
            if(ans<dp[i])
               ans = dp[i],end=i,begin=start;   //记录末尾位置
        }
        cout<<"Case "<<++num<<":"<<endl<<ans<<" "<<begin<<" "<<end<<endl;
        if(T)
            cout<<endl;
    }
    return 0;
}


http://acm.hit.edu.cn/hoj/problem/view?id=1760


//AC


#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<stdlib.h>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<iomanip>
#include<numeric>
//#include <istream>     //基本输入流
//#include <ostream>     //基本输出流
//#include <sstream>     //基于字符串的流
//#include <utility>     //STL 通用模板类
//#include <complex.h>   //复数处理
//#include <fenv.h>    //浮点环境
//#include <inttypes.h>  //整数格式转换
//#include <stdbool.h>   //布尔环境
//#include <stdint.h>   //整型环境
//#include <tgmath.h>   //通用类型数学宏
#define L(a,b,c) for(int a = b;a >= c;a --)
#define M(a,b,c) for(int a = b;a <= c;a ++)
#define N(a,b) memset(a,b,sizeof(a));
#define MAXX(a,b)   ((a)>(b)?a:b)
#define MINN(a,b)   ((a)<(b)?a:b)
const int MAX=1<<30;
const int MIN=-MAX;
const int N=10010;
using namespace std;
int dp[N],a[N];
int main()
{
    int n;
    while(scanf("%d",&n)&&n)
    {
        N(dp,0)
        M(i,1,n)
        cin>>a[i];
        int max=-1;
        M(i,1,n)
        {
            if(dp[i]>0)
                dp[i+1]=dp[i]+a[i];
            else
                dp[i+1]=a[i];
            if(dp[i+1]>max)
                max=dp[i+1];
        }
        if(max>0)
            printf("The maximum winning streak is %d.\n",max);
        else
            printf("Losing streak.\n");
    }
    return 0;
}

2.最大m子段和(HDU1024 , POJ2479)

已知有n个数,求m段不相交的子段权值之和最大,
状态转移方程:dp[i][j]表示以i为结尾元素的j个子段的数和
dp[i][j]=max(dp[i-1][j]+a[i],dp[i-k][j-1]+a[i]);其中(j-1<=k<=n-m+1)
此题实现这种思想:
for(i=1;i<=m;i++)
        {
             maxx=(-1)*(N*90);//初始化
            for(j=i;j<=n;j++)
            {
                num[j]=max(num[j-1]+a[j],pre[j-1]+a[j]);//其中now[j-1]表示的是以j-1结尾的元素i个子段的数和,pre[j-1]表示的是前j-1个元素中i-1个子段的数和
                pre[j-1]=maxx;//放在此处是为了实现pre[j-1]+a[j]中a[j]是一个独立的子段,那么此时就应该用的是i-1段
                if(num[j]>maxx)
                {
                    maxx=num[j];
                }
            }
        }


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


//AC

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<stdlib.h>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<iomanip>
#include<numeric>
//#include <istream>     //基本输入流
//#include <ostream>     //基本输出流
//#include <sstream>     //基于字符串的流
//#include <utility>     //STL 通用模板类
//#include <complex.h>   //复数处理
//#include <fenv.h>    //浮点环境
//#include <inttypes.h>  //整数格式转换
//#include <stdbool.h>   //布尔环境
//#include <stdint.h>   //整型环境
//#include <tgmath.h>   //通用类型数学宏
#define L(a,b,c) for(int a = b;a >= c;a --)
#define M(a,b,c) for(int a = b;a <= c;a ++)
#define N(a,b) memset(a,b,sizeof(a));
#define MAXX(a,b)   ((a)>(b)?a:b)
#define MINN(a,b)   ((a)<(b)?a:b)
const int MAX=1<<30;
const int MIN=-MAX;
const int N=1001000;
using namespace std;
int a[N],pre[N],num[N];
int main()
{
    int n,m;
    while(cin>>m>>n)
    {
        for(int i=1;i<=n;i++)
            cin>>a[i];
        N(num,0);
        N(pre,0);
        int maxx;
        M(i,1,m)
        {
            maxx=MIN;
            M(j,i,n)
            {
                num[j]=max(num[j-1]+a[j],pre[j-1]+a[j]);
                pre[j-1]=maxx;
                if(num[j]>maxx)
                    maxx=num[j];
            }
        }
        cout<<maxx<<endl;
    }
    return 0;
}


http://poj.org/problem?id=2479

//AC

(用C++输入超时)

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<stdlib.h>
#include<string>
#include<algorithm>
#define L(a,b,c) for(int a = b;a >= c;a --)
#define M(a,b,c) for(int a = b;a <= c;a ++)
#define N(a,b) memset(a,b,sizeof(a));
#define MAXX(a,b)   ((a)>(b)?a:b)
#define MINN(a,b)   ((a)<(b)?a:b)
const int MAX=1<<30;
const int MIN=-MAX;
const int N=50005;
using namespace std;
int a[N],pre[N],num[N];
int main()
{
    int n,T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        N(num,0);
        N(pre,0);
        int maxx;
        M(i,1,2)
        {
            maxx=MIN;
            M(j,i,n)
            {
                num[j]=max(num[j-1]+a[j],pre[j-1]+a[j]);
                pre[j-1]=maxx;
                if(num[j]>maxx)
                    maxx=num[j];
            }
        }
        cout<<maxx<<endl;
    }
    return 0;
}


3.最大子矩阵和

所谓万变不离其宗,这个的思路和上面是一样的. 维数增加了一维,所以可以考虑把它转化成一维的"基本问题". 我们可以先统计sum[i][j](以下假设下标从1开始) : 第i行,从开头到第j个元素的总值,这样, 第i行从第j个元素到第k个元素的总价值就是sum[i][k] - sum[i][j - 1]. 这个预处理的时间复杂度是O(N^2). 这时,这个问题就转化成了一维的最大子段和问题了: 枚举每一行中,第i到第j个元素(1 <= i <= j <= n),就可以把j - i + 1个元素的总和看成一个元素(转化的过程), 然后,对n个这样的元素求最大子段和即可. 这一部分的时间复杂度是O(N^3),因此总复杂度也为O(N^3).

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1074

//AC

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
#include<stdlib.h>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<list>
#include<queue>
#include<stack>
#include<iomanip>
#include<numeric>
//#include <istream>     //基本输入流
//#include <ostream>     //基本输出流
//#include <sstream>     //基于字符串的流
//#include <utility>     //STL 通用模板类
//#include <complex.h>   //复数处理
//#include <fenv.h>    //浮点环境
//#include <inttypes.h>  //整数格式转换
//#include <stdbool.h>   //布尔环境
//#include <stdint.h>   //整型环境
//#include <tgmath.h>   //通用类型数学宏
#define L(a,b,c) for(int a = b;a >= c;a --)
#define M(a,b,c) for(int a = b;a <= c;a ++)
#define N(a,b) memset(a,b,sizeof(a));
#define MAXX(a,b)   ((a)>(b)?a:b)
#define MINN(a,b)   ((a)<(b)?a:b)
const int MAX=1<<30;
const int MIN=-MAX;


using namespace std;
int dp[111][111],a[111][111];
int main()
{
    int n;
    while(cin>>n)
    {
        N(dp,0)
        M(i,1,n)    //输入矩阵对应位置的值
        M(j,1,n)
        cin>>a[i][j];
        M(i,1,n)
        {
            int t=0;
            M(j,1,n)    //对每一行的元素进行加和统计,存入dp数组中
            {
                t+=a[i][j];
                dp[i][j]=t;
            }
        }
        int ans,max=MIN;
        M(i,1,n)    //每一行的起始位置
        {
            M(j,i,n)    //每一行所能到达的位置
            {
                ans=0;      //记录每一步的值 
                M(k,1,n)    //用k表示行数
                {
                    int t=dp[k][j]-dp[k][i-1];
                    if(ans>0)
                        ans+=t;
                    else
                        ans=t;
                    if(ans>max)     //找最大值
                        max=ans;
                }
            }
        }
        cout<<max<<endl;    //输出最大值
    }
    return 0;
}


4.最大子立方体

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值