NYOJ 44 子串和
法一:TLE
#include<stdio.h>
int main()
{
int n,m;
scanf("%d",&m);
while(m--)
{
int i,j,a[1001];
int s[1001],best;
scanf("%d",&n);
s[0]=0;
for(i=1;i<=n;++i)
scanf("%d",&a[i]);
best=a[1];
for(i=1;i<=n;++i)
s[i]=s[i-1]+a[i];
for(i=1;i<=n;++i)
for(j=i;j<=n;++j)
if(best<=s[j]-s[i-1])
best=s[j]-s[i-1];
printf("%d\n",best);
}
return 0;
}
法二:AC
思路点拨:
dp[i]表示前i个元素,子串最大和,要求a[i]必在其中
状态转移方程:dp[i]=max(dp[i-1]+a[i]],a[i]) 判定条件:dp[i]>0???
这里用sum记录dp[i]
#include<stdio.h>
int main()
{
int N,n,a,sum,max;
scanf("%d",&N);
while(N--)
{
scanf("%d%d",&n,&sum);
max=sum;
while(--n)
{
scanf("%d",&a);
if(sum<0)//sum+a<a
sum = a;
else sum +=a;
if(max<sum) max= sum;
}
printf("%d\n",max);
}
return 0;
}
NYOJ 734 笨蛋难题四
法一:常规思路
#include<stdio.h>
#include<stdlib.h>
#define maxn 100000
int str[maxn+2];
int main()
{
int n,i,record,pos,ma,start;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
{
scanf("%d",&str[i]);
}
start=pos=1;
record=-99999999;
while(n-start>0)
{
ma=str[start];
for(i=start;i<n;i++)
{
/*搜寻段的最大值*/
if(ma<=str[i])
{
ma=str[i];
pos=i;
}
}
for(i=start-1;i<pos;i++)
{
if(record<(str[pos]-str[i]))
record=(str[pos]-str[i]);
}
start=pos+1;
}
printf("%d\n",record);
}
return 0;
}
法二:逆差
#include <stdio.h>
#include <stdlib.h>
int a[100000];
int max[100000]; //该位置以后到最大值
int main()
{
int n;
int i;
int result;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
scanf("%d",a+i);
max[n-1]=a[n-1];
for(i=n-2;i>=0;i--)
{
if(a[i]>max[i+1])
{
max[i]=a[i];
}
else
{
max[i]=max[i+1];
}
}
result=0;
for(i=0;i<n;i++)
if(result<max[i]-a[i])
result=max[i]-a[i];
printf("%d\n",result);
}
return 0;
}
法三:子串和
#include<stdio.h>
int main()
{
int a,b,di;
int sum,max;
int day;
while(scanf("%d",&day)!=EOF)
{
sum=0;
max=sum;
day--;
scanf("%d",&a);
while(day--)
{
scanf("%d",&b);
di=b-a;
if(sum<0) sum=di;
else sum+=di;
if(max<sum) max=sum;
a=b;
}
printf("%d\n",max);
}
return 0;
}
NYOJ 745 蚂蚁的难题(二)
环状子串和
#include<stdio.h>
#define MAX 50010
const long long INF=10000000000;
int a[MAX];
int main()
{
int n;
while (scanf("%d",&n)!=EOF)
{
int i,j;
long long sum,max=0,ans=0;
for (i=0;i<n;i++)
{
scanf("%d",&a[i]);
ans+=a[i];
}
if (ans<=0)
{
max=-INF;//取最大值
sum=0;
for (i=0,j=0;i<2*n-1;i++,j++)
{
if (j>=n)
j=0;
sum+=a[j];
if (0>sum)
{
sum=0;
}
if (max<sum)
max=sum;
}
printf("%lld\n",max);
}
else
{
sum=0;
max=INF;//取最小值
for (i=0,j=0;i<2*n-1;i++,j++)
{
if (j>=n)
j=0;
sum+=a[j];
if (0<sum)
{
sum=0;
}
if (max>sum)
max=sum;
}
printf("%lld\n",ans-max);
}
}
return 0;
}
回顾下NYOJ 44 子串和
状态:dp[1][j]有前j个数,组成1组的和的最大值,a[j]一定包括在所选子串中。
转移方程: dp[1][j]=Max(dp[1][j-1]+a[j] , a[j] )
a[j]包含在第1组里面或独立成组。
简约代码如下:
其中sum记录dp[1][j]最优解。
#include<stdio.h>
int main()
{
int n,m,i,max,sum;
scanf("%d",&n);
while(n--)
{
max=0;
scanf("%d",&m);
scanf("%d",&sum);
max=sum;
while(--m)
{
scanf("%d",&i);
if(sum<0) sum=i;
else sum+=i;
if(sum>max) max=sum;
}
printf("%d\n",max);
}
}
状态:dp[i][j]有前j个数,组成i组的和的最大值
转移方程: dp[i][j]=Max(dp[i][j-1]+a[j] , max(dp[i-1][k] ) + a[j] ) 0<k<j
a[j]包含在第i组里面或独立成组。
使用滚动数组 优化时间复杂度0(n^2).
#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define MAXN 1000000
#define INF 0x7fffffff
int dp[MAXN+10];
int mmax[MAXN+10];
int a[MAXN+10];
int main()
{
int n,m;
int i,j,mmmax;
while(scanf("%d%d",&m,&n)!=EOF)
{
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
mmax[i]=0;
dp[i]=0;
}
dp[0]=0;
mmax[0]=0;
for(i=1;i<=m;i++)
{
mmmax=-INF;//mmax用于表示最大值
//mmax[j-1]用于记录dp[0..i-1]中最大值
for(j=i;j<=n;j++)
{
dp[j]=max(dp[j-1]+a[j],mmax[j-1]+a[j]);
mmax[j-1]=mmmax;
mmmax=max(mmmax,dp[j]);
}
}
printf("%d\n",mmmax);
}
return 0;
}
POJ 1050 To the Max^NYOJ 104 最大和
二维矩阵最大值
转化成一维求解
状态:tmp[i][j][k] 表示第i行到j行 ,第k列的和
tmp2[i][j][1][k] 表示第i行到j行,矩阵必须包含第k列的最大和,类似于<子串和>
状态转移方程:tmp2[i][j][1][k] =max(tmp2[i][j][1][k-1]+tmp[i][j][k],tmp[i][j][k])
决策条件 :tmp2[i][j][1][k] 是否大于0
空间优化:
由于i,j可以通过枚举是实现,所求解只是最大和,故可用max作为中间量,保存最优子结构
空间可缩为二维即tmp3[1][k] <i,j枚举>
因为1表示起始位,常量,可缩为一维。
边界条件:tmp2[i][j][1][k]=tmp[i][j][k];
代码如下:
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAXN 100
using namespace std;
// 给定一个有数值的矩形,求出和最大的子矩形
// 经典的DP问题
/*
思路:枚举所有的行,然后将这些行的列进行相加构成一个串
然后再进行一次一维空间上的DP即可
*/
int N, M[MAXN+5][MAXN+5], sum[MAXN+5][MAXN+5], seq[MAXN+5];
int DP() {
int Max = 0x7fffffff+1;
for (int j = 1; j <= N; ++j) {
for (int i = 1; i <= N; ++i) {
sum[i][j] = sum[i-1][j] + M[i][j]; // sum[i][j] 表示1-i行上j列元素的和
}
}
for (int i = 1; i <= N; ++i) { // 枚举所有的行区间
for (int j = i; j <= N; ++j) {
for (int k = 1; k <= N; ++k) {
seq[k] = sum[j][k] - sum[i-1][k]; // 计算出[i,j]行之间k列的和值
}
for (int k = 1; k <= N; ++k) {
seq[k] = seq[k-1] + seq[k] > 0 ? seq[k-1] + seq[k] : 0;
Max = max(Max, seq[k]);
}
}
}
return Max;
}
int main() {
while (scanf("%d", &N) == 1) {
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= N; ++j) {
scanf("%d", &M[i][j]);
}
}
printf("%d\n", DP());
}
return 0;
}