dp-更难的矩阵取数问题

本文讨论了一个关于寻找二维矩阵中最大路径和的问题,矩阵中的元素代表奖励,路径从左上角到右下角,然后从右下角回到左上角。路径规则限制了方向的选择,并且通过动态规划方法解决了此问题。

题目:    

一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上。第1遍时只能向下和向右走,第2遍时只能向上和向左走。两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值。

 
例如:3 * 3的方格。

1 3 3
2 1 3
2 2 1

能够获得的最大价值为:17。1 -> 3 -> 3 -> 3 -> 1 -> 2 -> 2 -> 2 -> 1。其中起点和终点的奖励只计算1次。

分析:
   dp[x1][y1][x2][y2]表示2条并行的路到达(x1,y1),(x2,y2)获得的最大值
  如果(x1,y1) =/= (x2,y2)
   dp[x1][y1][x2][y2] = dp[x1'][y1'][x2'][y2'] + val[x1][y1] + val[x2][y2]
  否则
        dp[x1][y1][x2][y2] = dp[x1'][y1'][x2'][y2'] + val[x1][y1]
  观察知道x1+y1 = x2+y2 
  所以就可以把4维变成了3维。
  dp[steps][x1][x2] = dp[steps-1][x1'][x2'] + val[x1][y1] + val[x2][y2]

代码:
#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;

int a[210][210];
int dp[420][210][210];
int m,n;
bool inRange(int x,int y) {
	if(0 <= x && x < n && 0 <= y && y < m) {
		return true;
	}
	return false;
}
int dir[2][2] = {-1,0,0,-1};
int main(){
	freopen("in.txt","r",stdin);
	scanf("%d%d",&m,&n);
	for(int i=0;i<n;i++) {
		for(int j=0;j<m;j++) {
			scanf("%d",&a[i][j]);
		}
	}
	
	memset(dp,0,sizeof(dp));
	dp[0][0][0] = a[0][0];
	for(int tot=1;tot<n+m-1;tot++) {
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				
				for(int d1=0;d1<2;d1++) {
					for(int d2=0;d2<2;d2++) {
						int x1 = i + dir[d1][0];
						int y1 = tot - 1 - x1;
						int x2 = j + dir[d2][0];
						int y2 = tot -1 - x2;
						if(!inRange(x1,y1) || !inRange(x2,y2)) {
							continue;
						}
						if(i != j) 
							dp[tot][i][j] = max(dp[tot][i][j],dp[tot-1][x1][x2] + a[i][tot-i] + a[j][tot-j]);
						else
							dp[tot][i][j] = max(dp[tot][i][j],dp[tot-1][x1][x2] + a[i][tot-i]);
					}
				}
			}
		}
	} 
	
	printf("%d\n",dp[n+m-2][n-1][n-1]);
}




矩阵游戏是一个具有特定规则的游戏,给定一个 $n$ 行 $m$ 列的矩阵矩阵中的每个元素为非负整,游戏规则如下:每次须从每行各走一个元素,共 $n$ 个,$m$ 次后矩阵所有元素;每次走的各个元素只能是该元素所在行的行首或行尾;每次都有一个得分值,为每行的得分之;每行的得分 = 被走的元素值*$2^i$,其中 $i$ 表示第 $i$ 次(从 1 开始编号);游戏结束总得分为 $m$ 次得分之 [^2][^3]。 解决矩阵问题的方法算法如下: - **简化问题**:每行之间的并不互相影响,所以只需求每行的最大分,再将每行的最大分加起来即可,这样问题就转换成了求每一行的最大分,即简化为一个双端队列的问题 [^1][^5]。 - **动态规划算法**:由于每完一个以后,队列就会变成一个新的双端队列,而且无论顺序如何,最终都需要相同的,因此这是一个动态规划dp问题。该问题的核心思想是对于同一个子队列,接下来的操作不会受到之前的操作的影响,只要保证找到生成这个子队列的最优解法即可 [^1]。 - **状态表示**:可以用一个矩阵来表示的结果,$k$ 表示的步,当从前端候则往矩阵下边走,当从后端候则往右边走,若遇到走入同一个空格的情况则其最大值 [^1]。 - **状态转移方程**:$maxi[k][j] = max(maxi[k][j - 1] + a[i][j] * (ll)(pow(2, m - j + k)), maxi[k + 1][j] + a[i][k] * (ll)(pow(2, m - j + k)))$,其中 $maxi[k][j]$ 表示在第 $i$ 行,当前剩余元素区间为 $[k, j]$ 的最大得分,$a[i][j]$ 表示矩阵第 $i$ 行第 $j$ 列的元素 [^5]。 - **求解结果**:当走完 $m$ 步以后,会出现一个对角线型的结果图,只要沿着对角线找出最大值输出来即为题解 [^1]。 以下是一个简单的 Python 代码示例,用于说明状态转移方程的使用: ```python import math # 示例矩阵 matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] n = len(matrix) m = len(matrix[0]) total_score = 0 for i in range(n): # 初始化 dp dp = [[0] * m for _ in range(m)] for length in range(1, m + 1): for k in range(m - length + 1): j = k + length - 1 if length == 1: dp[k][j] = matrix[i][k] * (2 ** m) else: step = m - (j - k) dp[k][j] = max( dp[k][j - 1] + matrix[i][j] * (2 ** step), dp[k + 1][j] + matrix[i][k] * (2 ** step) ) total_score += dp[0][m - 1] print("最大得分:", total_score) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值