探寻宝藏-双进程DP(优化版)

本文介绍了一个关于机器人在迷宫中寻找宝物的问题,迷宫从左上角到右下角,机器人只能向下或向右移动,返回时只能向上或向左,且不走回头路。目标是最大化收集的宝物价值。作者使用动态规划来解决这个问题,通过降维优化算法,减少了无效运算,最终输出了最大宝物价值。
摘要由CSDN通过智能技术生成

题目

传说HMH大沙漠中有一个M*N迷宫,里面藏有许多宝物。某天,Dr.Kong找到了迷宫的地图,他发现迷宫内处处有宝物,最珍贵的宝物就藏在右下角,迷宫的进出口在左上角。当然,迷宫中的通路不是平坦的,到处都是陷阱。Dr.Kong决定让他的机器人卡多去探险。 但机器人卡多从左上角走到右下角时,只会向下走或者向右走。从右下角往回走到左上角时,只会向上走或者向左走,而且卡多不走回头路。(即:一个点最多经过一次)。当然卡多顺手也拿走沿路的每个宝物。 Dr.Kong希望他的机器人卡多尽量多地带出宝物。请你编写程序,帮助Dr.Kong计算一下,卡多最多能带出多少宝物。

输入第一行:
K 表示有多少组测试数据。
接下来对每组测试数据:
第1行: M N
第2~M+1行: Ai1 Ai2 ……AiN (i=1,……,m)
【约束条件】
2≤k≤5 1≤M, N≤50 0≤Aij≤100 (i=1,….,M; j=1,…,N)
所有数据都是整数。 数据之间有一个空格。
输出
对于每组测试数据,输出一行:机器人卡多携带出最多价值的宝物数

样例输入
2
2 3
0 10 10
10 10 80
3 3
0 3 9
2 8 5
5 7 100
样例输出
120
134

分析

这道题的意思是一个人从迷宫左上角走到右下角,再返回去,不能走回头路,除了起点和终点外,其他地方只经过一次。求可以拿走的宝物价值的最大值。
这是一个最值问题。具有最优子结构特点,如果想要走完全程宝物价值最大,那么每走一步拿到的宝物价值都得最大。所以我们可以考虑用动态规划来做。
假设出发点为A终点为B,先只看A到B的过程,这个过程的状态是dp [i][j]表示走到(i,j)位置时可以拿到的宝物价值的最大值。状态转移变量是i,j的递增,也就是移动,这个移动方向有两种,向下和向右,所以,在状态转移过程中有两种选择,需要从这两种选择中找出最优解来做出决策。
max(dp[i-1][j],dp[i][j-1])代表做决策的过程。
状态转移:
(i,j)位置宝物价值最大值=(i-1, j) ,(i. j-1)两个位置宝物价值最大值中的较大一个+(i.j)处宝物价值。所以可以得出状态转移方程:
dp[i][j]= max(dp[i-1][j].dp[i]lj-1])+ a[i][j];

B到A的过程同理,所以,我们其实可以看做是两个人同时从A出发,走到B拿宝物。用i,j表示第一个人坐标,p,q表示第二个人坐标,类比上面一个人的情况,可以得出两个人的状态转移方程。状态是dp[i][j][p][q],表示第一个人走到(i,j)位置,第二个人走到(p,q)位置时两人可以拿到的宝物价值之和的最大值。状态转移变量是i,j,p,q的递增,也就是两个人的移动,一个人移动方向有两种,那么两个人就有四种,全向下,全向右,一下一右,所以,在状态转移过程中有四种选择,需要从这四种选择中找出最优解来做出决策。所以可以得出状态转移方程:
dp[i][j][p][q] = max(dp[i-1][i][p-1][q],dp[i-1][j][p][q-1],dp[i]lj-1][p-1][q],dp[i][j-1][p][q-1]) +a[i][j]+a[p][q].
这时,问题就解决了,接下来讲一下我对算法的优化。
1.
因为从A点开始移动的,那么i+j,p+q等于移动的步数。因为两人每次只能移动一步,步数永远相等,得到i+j = p+q=step,便可通过此等式来降维,通过移项来用p.q. j表示i, i=p+q-j。即可将四重循环缩减为三重循环。时间复杂度由n4次方减少为n3次方,同时空间复杂度也同理缩小。
2.
减少无效运算,通过输出dp表时发现,存在因为组合问题带来的无效运算,原因是第一个人走第一条路,第二个人走第二条与第一个人走第二条,第二个人走第一条需要算两次,但得出结果只需要一次即可,便有了一次无效运算。

运行截图

在这里插入图片描述

代码

#include<iostream>
#include<algorithm>
using namespace std;
int map[51][51];
int dp[102][51][51];
int main() {
 int k;
 cin >> k;
 while (k--) {
  int m, n;
  cin >> m >> n;
  memset(map, 0, sizeof(map));
  memset(dp, 0, sizeof(dp));
  int i, j;
  for (i = 1; i <= m; i++) {
   for (j = 1; j <= n; j++) {
    cin >> map[i][j];
   }
  }
  int step, h1, h2, l1, l2;
  for (step = 2; step <= m + n ; step++) {
   for (h1 = 1; h1 <= m; h1++) {
    for (h2 = h1; h2 <= m; h2++) {
     l1 = step - h1;
     l2 = step - h2;
     if (l1 <= 0 || l2 <= 0 || l1 > n || l2 > n)
      continue;
     dp[step][h1][h2] = max(max(dp[step - 1][h1][h2], dp[step - 1][h1][h2 - 1]), max(dp[step - 1][h1 - 1][h2], dp[step - 1][h1 - 1][h2 - 1])) + map[h1][l1] + map[h2][l2];
     if (h1 == h2) {
      dp[step][h1][h2] = dp[step][h1][h2] - map[h1][l1];
     }
    }
   }
  }
  cout << dp[m + n][m][m] << endl;
  for (step = 2; step <= m + n; step++) {
   for (h1 = 1; h1 <= m; h1++) {
    for (l1 = 1; l1 <= m; l1++) {
     cout << dp[step][h1][l1] << " ";
    }
    cout << endl;
   }
   cout << endl;
  }
 }
 return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值