Acwing1027. 方格取数 python实现

1. 题目描述

题目链接

N*N方格中某些地方有正整数,其他都为0。

只能向右或者向下走,一个数只能取一次

问从左上角到右下角走两次,如何走能使得取得的数字最大

2. 题目思路

  1. 是在摘花生这道题进行了扩展,变成了两条路径之和最大为多少

  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)的所有路径的最大值

  3. 如何处理 ”同一个格子不能重复选择“

    • 什么时候两条路径会重合?只有在两条路经过的步数一样时才可能到达同一格子

      因此,两条路径同时走(而不是第一次走完再走第二次),即i1 + j1 == i2 + j2,在这个等式下,两条路径确实可能重合

      注意:

      1. 不用两次DP是因为不满足贪心原理,因为分两次不满足局部最优为全局最优
    • 由上面得出,其实可以从四个状态(i1, i2, j1, j2)变成三个状态:k,i1,i2。即f[k, i1, i2]表示从(1,1)分别到(i1, k - i1) (i2, k - i2)的所有路径的最大值

      注意:

      1. 这里化简成k是因为我们改成第二次紧接着第一次走(即两条路径同时走),而不是第一次走完再走第二次。两个人都走k步,包含了可能走到一个点,也可能没走到同一个点,这两种情况都包含了
      2. i1 + j1 == i2 + j2是一定成立的,而 i1 == i2 是可能发生的,这是到达同一个格子的情况
  4. 状态计算?

    • 最后一步的划分:

      • 第一条路径最后一步向下走,第二条路径最后一步向右走(简写为一下二右)
      • 一下二下
      • 一右二右
      • 一右二下
    • 每一类的计算

      • 以“一下二下”为例:

        image

    • 最后四种分类的最大值就是f[k, i1, i2]的值

  5. 初始化和遍历顺序

    • 数组开辟空间记得是\(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]) 
posted @ 2022-05-22 15:42  要兵长还是里维  阅读(99)  评论(0编辑  收藏  举报