poj1050-To The Max DP求解

本文探讨了如何利用动态规划解决二维矩阵中的最大子矩阵和问题,通过将问题转化为一维最优子序列问题,提出了高效算法。文章详细解释了暴力枚举与动态规划两种方法,展示了动态规划在降低时间复杂度方面的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

poj1050 to the max 问题可简要描述如下:
给定一个整数n(n<100),之后输入n*n个数字组成一个方阵,在方阵内部可以选出一些小矩阵,求在方阵内部可以选出的小矩阵所包含数字的和的最大值
这个问题是一个典型的动态规划的问题,而且,和最优子序列问题实质上是一样的。

最优子序列问题

最优子序列问题是指,给定一组序列,求解其子列中所有元素和的最大值。子列为其中某个元素到另一个元素之间的连续的一组值。比如给定数组a[6]={-3,5,2,-4,9,-6},其中{5,2,-4}就可以作为a的一个子列,不过,显然可以看出此例最优的子列应该为{5,2,-4,9},和是12。下面分析其解决算法:

暴力枚举

显然,可以通过枚举的方法,把整个数组的每一种子列的和全搜索一遍,并记录最大值输出,相关代码如下

int maxnum=0;//记录和的最大值
int a[]={-3,5,2,-4,9,-6};
int n=sizeof(a)/sizeof(int);
for(int i=0;i<n;i++){
	int sum=0;
	for(int j=i;j<n;j++){
		sum+=a[j];//求出从a[i]到a[j]子列的和
		if(sum>maxnum)
		maxnum=sum;
	}
}	

易看出,此算法的时间复杂度是O(n2)

动态规划

首先定义一状态量:每个位置 i 的最优子序列 bi 的值就是以此数为结尾的所有子序列的值的最大值;也就是对a[6]={-3,5,2,-4,9,-6},第二个位置的最优子序列值就是{-3,5}与{5}两个子序列其中值最大的那个,即b2=5。那么,不难理解,若已知 bi ,则有递推公式: bi+1 = bi + a [ i+1 ],bi >= 0 或 bi+1 = a [ i+1 ], bi<0 。
故求解所有子序列中的最大值,即为求解序列b1 到bn 的最大值,代码如下:

int a[]={-3,5,2,-4,9,-6};
int n=sizeof(a)/sizeof(int);
int maxhere=0, maxnum=0;		//maxhere记录的是当前位置的最优子序列值,没有必要另用数组保存
	for (int i = 0; i < n; i++){
		if (maxhere >= 0)
			maxhere += a[i];
		else
			maxhere = a[i];
		if (maxofnum < maxhere)
			maxofnum = maxhere;
			}

不难看出时间复杂度是O(n)

poj1050

回到问题poj1050,题目说要求得内部矩阵的值的最大值,如果要暴力枚举的话,从列的角度需要两次循环,行的角度一样,整个 程序较为复杂,因此也要使用dp算法。
事实上按照如上定义的状态量来计算也并不简单,因为二维之间的状态量之间没有一维那样明显的递推关系。可以将二者结合起来,将从第i行到第j行之间的数字按列相加,得一列数字,之后使用上面代码,计算此列数字中的最大子列值,有如下代码:

int maxsequence(int b[], int nb){ 		//计算一组数中的最优子序列的值
	int maxhere=0, maxofnum=0;
	for (int i = 0; i < nb; i++){
		if (maxhere >= 0)
			maxhere += b[i];
		else
			maxhere = b[i];
		if (maxofnum < maxhere)
			maxofnum = maxhere;
	}
	return maxofnum;
}
for (int i = 0; i < n; i++){
		int temp[110] = { 0 };		//将第i行到第j行的数据按列相加,记录在temp[]中
		for (int j = i; j < n; j++){
			for (int k = 0; k < n; k++){
				temp[k] += a[j][k];
			}
			if (max < maxsequence(temp, n))
				max = maxsequence(temp, n);
		}
	}

AC代码

#include<iostream>
using namespace std;
int maxsequence(int b[], int nb){
	int maxhere=0, maxofse=0;
	for (int i = 0; i < nb; i++){
		if (maxhere >= 0)
			maxhere += b[i];
		else
			maxhere = b[i];
		if (maxofse < maxhere)
			maxofse = maxhere;
	}
	return maxofse;
}
int main(){
	int n;
	cin >> n;
	int **a = new int *[n];
	for (int i = 0; i < n; i++)
		a[i] = new int[n];
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++){
			cin >> a[i][j];
		}
	int max = 0;
	for (int i = 0; i < n; i++){
		int temp[110] = { 0 };
		for (int j = i; j < n; j++){
			for (int k = 0; k < n; k++){
				temp[k] += a[j][k];
			}
			if (max < maxsequence(temp, n))
				max = maxsequence(temp, n);
		}
	}
	for (int i = 0; i < n; i++)
		delete[]a[i];
	delete[]a;
	cout << max<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值