问题链接:HDU1003 Max Sum。
问题简述:参见上述链接。
问题分析:计算最大子段和问题,是一个经典的动态规划问题。
关键思路:如果上一次的子段和+当前遍历到的这个元素数值<当前遍历到的这个元素数值(其实也就是上次的sum<0),那么你上一次的子段和就拖累了当前元素,就应把sum新设为当前元素值,让当前元素去开始它的新天地。若>,则帮助了当前元素,就把当前元素并到上次sum中继续前进。
- #include <iostream>
- using namespace std;
- int main()
- {
- // max, maxstart, maxend为一组,是已经求得的最大子段和
- // sum, sumstart, j为一组,是当前正在进行计算的最大子段和
- // 当前的子段不再单调增大时,则重新开启一个新的子段
- int t, n, now, max, maxstart, maxend, sum, sumstart;
- cin >> t;
- for(int i=1; i<=t; i++) {
- cin >> n;
- // 一个元素时,它就是目前的最大子段和;最大子段和的起始和终止分别是maxstart和end
- cin >> now;
- max = sum = now;
- maxstart = maxend = sumstart = 1;
- for(int j=2; j<=n; j++) {
- cin >> now;
- if(now > now + sum)
- // 不单调递增时(之前子段和为负),把当前的元素预存为另外的一个子段
- sum = now, sumstart = j;
- else
- // 单调递增
- sum += now;
- // 当前正在进行计算的最大子段和超过之前的最大子段和,则重置最大子段和
- if(sum > max)
- max = sum, maxstart = sumstart, maxend = j;
- }
- cout << "Case " << i << ":" << endl;
- cout << max << " " << maxstart << " " << maxend << endl;
- if(i != t)
- cout << endl;
- }
- return 0;
- }
【求最大子矩阵和】
问题描述
给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。
其中,A的子矩阵指在A中行和列均连续的一块。
其中,A的子矩阵指在A中行和列均连续的一块。
输入格式
输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
接下来n行,每行m个整数,表示矩阵A。
接下来n行,每行m个整数,表示矩阵A。
输出格式
输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。
样例输入
3 3
-1 -4 3
3 4 -1
-5 -2 8
-1 -4 3
3 4 -1
-5 -2 8
样例输出
10
样例说明
取最后一列,和为10。
数据规模和约定
对于50%的数据,1<=n, m<=50;b
对于100%的数据,1<=n, m<=500,A中每个元素的绝对值不超过5000。
对于100%的数据,1<=n, m<=500,A中每个元素的绝对值不超过5000。
问题分析:
这是一个计算最大子矩阵和的问题。
可以将该问题转化为计算最大子段和问题,是一个经典的动态规划问题。
大致思路:和上一题的相同点在于都用了一个“拖累”思想,也就是动态规划的状态转移的灵活应用。
不同点在于它是要求子“矩阵”和,长宽未知要遍历——即,起点行和终点行未知要遍历,起点列和终点列未知要遍历。s数组是用来记录起点行到终点行的每一竖条的数值和的,dp[k]是用来记录不同的子矩阵和的(需要相邻竖条相加),maxsum是用来保存最大sum值的。
每一次变换起点行,s数组也应重新初始化为0。
- #include <iostream>
- #include <cstring>
- using namespace std;
- int a[510][510],s[510],dp[510];
- int main()
- {
- int n,m,i,j,k,temp;
- int max=-100000000;
- cin >> n >> m;
- for(i=1;i<=n;i++)
- {
- for(j=1;j<=m;j++)
- {
- cin >> a[i][j];
- }
- }
- for(i=1;i<=n;i++) //起点行
- {
- memset(s,0,sizeof(int)*510);
- for(j=i;j<=n;j++) //终点行
- {
- for(k=1;k<=m;k++) //列上的遍历
- {
- s[k] += a[j][k];
- if(dp[k-1] > 0) //状态转移——“拖累思想”。若dp[k-1]>0则不会拖累当前数。
- {
- dp[k] = dp[k-1]+s[k];
- }else{
- dp[k] = s[k];
- }
- if(dp[k]>max)
- {
- max = dp[k];
- }
- }
- }
- }
- cout << max;
- return 0;
- }