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]
显然这个模型空间复杂度可以压缩至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.最大子立方体