【蓝桥杯-动态规划】求最大子段和+最大子矩阵和

问题链接HDU1003 Max Sum

问题简述:参见上述链接

问题分析:计算最大子段和问题,是一个经典的动态规划问题。

关键思路:如果上一次的子段和+当前遍历到的这个元素数值<当前遍历到的这个元素数值(其实也就是上次的sum<0),那么你上一次的子段和就拖累了当前元素,就应把sum新设为当前元素值,让当前元素去开始它的新天地。若>,则帮助了当前元素,就把当前元素并到上次sum中继续前进。

  1. #include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     // max, maxstart, maxend为一组,是已经求得的最大子段和  
  8.     // sum, sumstart, j为一组,是当前正在进行计算的最大子段和  
  9.     // 当前的子段不再单调增大时,则重新开启一个新的子段  
  10.     int t, n, now, max, maxstart, maxend, sum, sumstart;  
  11.     cin >> t;  
  12.     for(int i=1; i<=t; i++) {  
  13.         cin >> n;  
  14.   
  15.         // 一个元素时,它就是目前的最大子段和;最大子段和的起始和终止分别是maxstart和end  
  16.         cin >> now;  
  17.         max = sum = now;  
  18.         maxstart = maxend = sumstart = 1;  
  19.   
  20.         for(int j=2; j<=n; j++) {  
  21.             cin >> now;  
  22.   
  23.             if(now > now + sum)  
  24.                 // 不单调递增时(之前子段和为负),把当前的元素预存为另外的一个子段  
  25.                 sum = now, sumstart = j;  
  26.             else  
  27.                 // 单调递增  
  28.                 sum += now;  
  29.   
  30.             // 当前正在进行计算的最大子段和超过之前的最大子段和,则重置最大子段和  
  31.             if(sum > max)  
  32.                 max = sum, maxstart = sumstart, maxend = j;  
  33.         }  
  34.   
  35.         cout << "Case " << i << ":" << endl;  
  36.         cout << max << " " << maxstart << " " << maxend << endl;  
  37.         if(i != t)  
  38.             cout << endl;  
  39.     }  
  40.   
  41.     return 0;  
  42. }  




【求最大子矩阵和】

问题描述
  给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。

  其中,A的子矩阵指在A中行和列均连续的一块。
输入格式
  输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
  接下来n行,每行m个整数,表示矩阵A。
输出格式
  输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。
样例输入
3 3
-1 -4 3
3 4 -1
-5 -2 8
样例输出
10
样例说明
  取最后一列,和为10。
数据规模和约定
  对于50%的数据,1<=n, m<=50;b
  对于100%的数据,1<=n, m<=500,A中每个元素的绝对值不超过5000。

问题分析

这是一个计算最大子矩阵和的问题。

可以将该问题转化为计算最大子段和问题,是一个经典的动态规划问题。

大致思路

和上一题的相同点在于都用了一个“拖累”思想,也就是动态规划的状态转移的灵活应用。

不同点在于它是要求子“矩阵”和,长宽未知要遍历——即,起点行和终点行未知要遍历,起点列和终点列未知要遍历。s数组是用来记录起点行到终点行的每一竖条的数值和的,dp[k]是用来记录不同的子矩阵和的(需要相邻竖条相加),maxsum是用来保存最大sum值的。

每一次变换起点行,s数组也应重新初始化为0。

  1. #include <iostream>    
  2. #include <cstring>    
  3. using namespace std;    
  4. int a[510][510],s[510],dp[510];  
  5. int main()  
  6. {  
  7.     int n,m,i,j,k,temp;  
  8.     int max=-100000000;   
  9.     cin >> n >> m;  
  10.     for(i=1;i<=n;i++)  
  11.     {  
  12.         for(j=1;j<=m;j++)  
  13.         {  
  14.             cin >> a[i][j];  
  15.         }  
  16.     }  
  17.       
  18.     for(i=1;i<=n;i++)  //起点行
  19.     {  
  20.         memset(s,0,sizeof(int)*510);  
  21.         for(j=i;j<=n;j++)   //终点行
  22.         {  
  23.             for(k=1;k<=m;k++)   //列上的遍历
  24.             {  
  25.                 s[k] += a[j][k];  
  26.                           
  27.                 if(dp[k-1] > 0)     //状态转移——“拖累思想”。若dp[k-1]>0则不会拖累当前数。
  28.                 {  
  29.                      dp[k] = dp[k-1]+s[k];    
  30.                 }else{  
  31.                      dp[k] = s[k];  
  32.                 }  
  33.               
  34.                 if(dp[k]>max)  
  35.                 {  
  36.                     max = dp[k];  
  37.                 }  
  38.             }  
  39.         }  
  40.     }  
  41.       
  42.     cout << max;  
  43.       
  44.     return 0;  
  45. }   

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值