NYOJ 44 子串和&NYOJ 734 笨蛋难题四&NYOJ 745 蚂蚁的难题(二) &NYOJ742 子串和再续&POJ 1050 To the Max^NYOJ 104 最大和

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);
	}
}        

NYOJ742 子串和再续

状态: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;    
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值