Acwing1027. 方格取数 python实现
1. 题目描述
N*N方格中某些地方有正整数,其他都为0。
只能向右或者向下走,一个数只能取一次
问从左上角到右下角走两次,如何走能使得取得的数字最大
2. 题目思路
-
是在摘花生这道题进行了扩展,变成了两条路径之和最大为多少
-
如何从走一次扩展到走两次?
-
f[i, j] 表示从(1,1)走到(i, j)的所有路径的最大值
\(f[i,j] = max(f[i,j-1]+g[i,j],f[i-1,j]+g[i,j]\)
-
类推——如果走两次:
f[i1, j1, i2, j2] 表示从(1,1)分别走到(i1, j1)、(i2, j2)的所有路径的最大值
-
-
如何处理 ”同一个格子不能重复选择“
-
什么时候两条路径会重合?只有在两条路经过的步数一样时才可能到达同一格子
因此,两条路径同时走(而不是第一次走完再走第二次),即i1 + j1 == i2 + j2,在这个等式下,两条路径确实可能重合
注意:
- 不用两次DP是因为不满足贪心原理,因为分两次不满足局部最优为全局最优
-
由上面得出,其实可以从四个状态(i1, i2, j1, j2)变成三个状态:k,i1,i2。即f[k, i1, i2]表示从(1,1)分别到(i1, k - i1) (i2, k - i2)的所有路径的最大值
注意:
- 这里化简成k是因为我们改成第二次紧接着第一次走(即两条路径同时走),而不是第一次走完再走第二次。两个人都走k步,包含了可能走到一个点,也可能没走到同一个点,这两种情况都包含了
- i1 + j1 == i2 + j2是一定成立的,而 i1 == i2 是可能发生的,这是到达同一个格子的情况
-
-
状态计算?
-
最后一步的划分:
- 第一条路径最后一步向下走,第二条路径最后一步向右走(简写为一下二右)
- 一下二下
- 一右二右
- 一右二下
-
每一类的计算
-
以“一下二下”为例:
-
-
最后四种分类的最大值就是f[k, i1, i2]的值
-
-
初始化和遍历顺序
-
数组开辟空间记得是\(f[N*2][N][N]\),因为k表示的是横纵坐标之和
-
求的是最大值,将\(f[N*2][N][N]\)直接都初始化为0
-
k,从2开始遍历(因为点(1,1)时k就为2了)
i1和i2,都是从1到n
三个变量的遍历谁外层谁内层都无所谓,结果都会遍历到,但是最好是k放在最外层(因为k遍历的长度最大)
- 记得要判断是否越界(比如k=2,i1取到n,那j1的结果就是负的,已经越界了就不考虑,继续下一个遍历)
-
3. 代码实现
n = int(input())
w = [[0 for _ in range(n + 1)] for _ in range(n + 1)]
dp = [[[0 for _ in range(n + 1)]for _ in range(n + 1)]for _ in range(n * 2 + 1)]
while True:
a, b, c = map(int, input().split())
if a == 0 and b == 0 and c == 0: break
w[a][b] = c
for k in range(2, n * 2 + 1):
for i1 in range(1, n + 1):
for i2 in range(1, n + 1):
j1 = k - i1
j2 = k - i2
if j1 >= 1 and j1 <= n and j2 >= 1 and j2 <= n:
t = w[i1][j1]
if i1 != i2:
t += w[i2][j2] #不相同说明不重合
dp[k][i1][i2] = max(dp[k][i1][i2], dp[k - 1][i1 - 1][i2 - 1] + t) # 一下二下
dp[k][i1][i2] = max(dp[k][i1][i2], dp[k - 1][i1 - 1][i2] + t) # 一下二右
dp[k][i1][i2] = max(dp[k][i1][i2], dp[k - 1][i1][i2 - 1] + t) # 一右二下
dp[k][i1][i2] = max(dp[k][i1][i2], dp[k - 1][i1][i2] + t) # 一右二右
print(dp[n * 2][n][n])