题目
传说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;
}