题目传送门

一、走两次行不行

这是一种类似贪心的解法,第一次选择最大的,然后把最大路径上的数字都置为空,第二次再选择最大的。
这就是只见树木不见森林的方法了:
第一次走为局部最优并且也对第二次走造成了影响,第二次走是在第一次影响下所能走的局部最优,不具备“无后效性”,因此分开两次走并不是全局最优解。

不过如果先取一次最大路径、抹掉数字后再取一次最大路径(即两次\(dp\))是错的,可以举一个反例来证明,

9 0 3
0 9 2
0 2 0

最长路径是\(9+9+2=20\),假设这次走的路线是第一行的\(9\)、第二行的\(9\)\(2\),那么在第二次走的时候,地图变成

0 0 3
0 0 0
0 2 0

显然最长路径是\(3\),总和为\(23\)

然而这不是最优解,最优解是第一次走第一行的\(9\)、第二行的\(9\)、第三行的\(2\)(仍是\(20\)),但第二次可以走第一行的\(3\)、第二行的\(2\),得到\(5\),总和达到\(25\)

二、四维状态表示

1、状态表示:

\(f[x1][y1][x2][y2]\)表示第一条路径走到\(x1,y1\)、第二条路径走到\(x2,y2\)时的最大值

2、状态转移:

那么它的上一个状态只能是下面四个中的某一个:
\(x1-1,x2-1\)(下下)
\(x1-1,y2-1\)(下右)
\(y1-1,x2-1\)(右下)
\(y1-1,y2-1\)(右右)
那到底是从哪个状态迁移来的呢?因为我们想要取此状态的最大值,所以很显然,四个前序状态,哪个数大要哪个。

#include <bits/stdc++.h>

using namespace std;
const int N = 15;

int n;              //方格的宽度和高度
int w[N][N];        //每个方格里面的数字
int f[N][N][N][N];  //四维的DP数组

int main() {
    cin >> n;
    //接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
    int a, b, c;
    //一行 0 0 0 表示结束。
    while (cin >> a >> b >> c, a || b || c) w[a][b] = c;
    //开始递推
    for (int x1 = 1; x1 <= n; x1++)
        for (int y1 = 1; y1 <= n; y1++)
            for (int x2 = 1; x2 <= n; x2++)
                for (int y2 = 1; y2 <= n; y2++)
                    if (x1 + y1 == x2 + y2) {
                        //PK获取到最优的上一个状态
                        int t = f[x1 - 1][y1][x2 - 1][y2];
                        t = max(t, f[x1][y1 - 1][x2][y2 - 1]);
                        t = max(t, f[x1 - 1][y1][x2][y2 - 1]);
                        t = max(t, f[x1][y1 - 1][x2 - 1][y2]);
                        //加上这个点的数值
                        f[x1][y1][x2][y2] = t + w[x1][y1];
                        //如果这个点没有被重复走,那么再加一次
                        if (x1 != x2 && y1 != y2) f[x1][y1][x2][y2] += w[x2][y2];
                    }
    printf("%d", f[n][n][n][n]);
}

优化一层循环的办法:

#include <bits/stdc++.h>

using namespace std;
const int N = 15;

int n;              //方格的宽度和高度
int w[N][N];        //每个方格里面的数字
int f[N][N][N][N];  //四维的DP数组

int main() {
    cin >> n;
    //接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
    int a, b, c;
    //一行 0 0 0 表示结束。
    while (cin >> a >> b >> c, a || b || c) w[a][b] = c;
    //开始递推
    for (int x1 = 1; x1 <= n; x1++)
        for (int y1 = 1; y1 <= n; y1++)
            for (int x2 = 1; x2 <= n; x2++) {
                //优化后可以减少一维的循环
                int y2 = x1 + y1 - x2;
                if (y2 >= 1 && y2 <= n) {//要小心点,别整出一个不符合实际情况的数
                    //PK获取到最优的上一个状态
                    int t = f[x1 - 1][y1][x2 - 1][y2];
                    t = max(t, f[x1][y1 - 1][x2][y2 - 1]);
                    t = max(t, f[x1 - 1][y1][x2][y2 - 1]);
                    t = max(t, f[x1][y1 - 1][x2 - 1][y2]);
                    //加上这个点的数值
                    f[x1][y1][x2][y2] = t + w[x1][y1];
                    //如果这个点没有被重复走,那么再加一次
                    if (x1 != x2 && y1 != y2) f[x1][y1][x2][y2] += w[x2][y2];
                }

            }

    printf("%d", f[n][n][n][n]);
}

三、三维的解法

#include <bits/stdc++.h>

using namespace std;
const int N = 15;

int n;
int w[N][N];
int f[N * 2][N][N];

int main() {
    cin >> n;
    //接下来的每行有三个整数,第一个为行号数,第二个为列号数,第三个为在该行、该列上所放的数。
    int a, b, c;
    //一行 0 0 0 表示结束。
    while (cin >> a >> b >> c, a || b || c) w[a][b] = c;

    //左上角是(1,1),k表示两个小朋友所在位置的x+y的和,最多是2*n
    //注意:这里k不是走过的长度!因为在原地它就是2~~~
    for (int k = 2; k <= 2 * n; k++)
        for (int x1 = 1; x1 <= n; x1++)//第一个小朋友竖着走的距离
            for (int x2 = 1; x2 <= n; x2++) {//第二个小朋友竖着走的距离
                int y1 = k - x1, y2 = k - x2;//计算横着走的距离
                //不能出界,只走有效的位置
                if (y1 >= 1 && y1 <= n && y2 >= 1 && y2 <= n) {
                    //PK获取到最优的上一个状态
                    int t = f[k - 1][x1 - 1][x2];
                    t = max(t, f[k - 1][x1 - 1][x2 - 1]);
                    t = max(t, f[k - 1][x1][x2 - 1]);
                    t = max(t, f[k - 1][x1][x2]);
                    //将本位置的数值加上
                    f[k][x1][x2] = t + w[x1][y1];
                    //如果不是重复的位置,还可以继续加上
                    if (x1 != x2 && y1 != y2) f[k][x1][x2] += w[x2][y2];
                }
            }
    //输出DP的结果
    printf("%d\n", f[2 * n][n][n]);
    return 0;
}

相关文章:

  • 2021-12-05
  • 2021-06-08
  • 2021-09-20
  • 2021-12-14
  • 2022-01-21
猜你喜欢
  • 2022-12-23
  • 2021-07-03
  • 2021-06-14
  • 2021-08-06
  • 2021-08-11
相关资源
相似解决方案