HBU训练营【动态规划DP】——奥马尔·爱糖果 (30分)

奥马尔喜欢吃很多糖果,但不幸的是,大部分的糖果都不健康。所以他的父母找到了一种方法,给每个糖果打分,分数越高,糖果就越健康(分数是一个整数,可以是正的、零的或负的)。一个孩子和他的父母买了一些糖果,他们到了卖糖果的地方,所有的糖果都被存储在一个N*M的二维网格中,每一行有M个糖果。行从上到下从1到N编号,列从左到右从1到M编号,每个单元格包含一个糖果健康值。奇怪的是,这个二维网格中的糖果的健康值总是从上到下、从左到右依次增大,如下图

奥马尔想买完一个子矩阵中的所有糖果,请你编程序求出他所能买得的最大的糖果健康值。

输入格式:
输入的第一行是单个整数T,测试用例的数量(1≤T≤100)。每个测试用例开始一行包含空格隔开两个整数N M (1≤N,M≤1000)代表糖果网格的尺寸,紧随其后的是N行,每一个包含M个空格隔开整数,代表这一行糖果的健康值。网格中的每个整数将不小于- 2000,也不大于2000。

输出格式:
对于每个测试用例,打印包含单个整数的一行,该整数表示从非空子矩形中可以获得的健康值的最大总和。

输入样例:
在这里给出一组输入。例如:

1
3 3
-4 -2 -1
-3 2 3
1 4 5

输出样例:
在这里给出相应的输出。例如:

14

唉我天,动态规划好难,好难,好难,懂一遍又不懂,似乎陷入死循环。。。。
由题意可知,较大的数存放在矩阵的后部分,而较小的存放在前部分。动态规划将大问题划分为小问题,从而一步步获取最优解,下一个子阶段的求解建立在上一个子阶段的解的基础上,通过填表的方式来逐步推进,得到最优解。因此将矩阵倒叙输入,这样顺序就与普通的动态规划一样了,再进行探测,下一步直接建立在上一步的基础上,也正因此,让表的第一行第一列全部置为0(其实初始化的时候已经置0),存储当前最大值得为数组dp,存储矩阵元素的为res,一一对应,所以均从1开始遍历。注意利用上一次结果求解当前解时,记得减去重复的部分,即dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + res[i][j];可自行按照循环填表得出该规律,由于假如当前元素可能使结果变大或变小(比如遇到负数),所以要选取最大的,max函数
注意,由于输入多组数据,所以dp和res数组都要重新置0,刚开始这里就错了,二维数组全部初始化为0可以用循环,可以用指针(首先把行置0,再逐步对列,因为二维数组中当前行的第一个元素代表着这一行的地址起始),都挺麻烦的,建议用memset,添加头文件<string.h>c++的话可以填这个或者

链接memset用法

#include <iostream>
#include <cstring>
using namespace std;

int dp[1001][1001],res[1001][1001];

int main(){
	ios::sync_with_stdio(false);
	int t,n,m;
	cin >> t;
	for(int p = 0;p<t;p++){
		memset(res,0,sizeof(res));
		memset(dp,0,sizeof(dp));
		int mm = -2001;
		cin >> n >> m;
		for(int i = n;i>=1;i--){
			for(int j = m;j>=1;j--){
				cin >> res[i][j];
			}
		}
		for(int i = 1;i<=n;i++){
			for(int j = 1;j<=m;j++){
				dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + res[i][j];
				mm = max(dp[i][j],mm);
			}
		}
		cout << mm << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45845039/article/details/107953274