zoukankan      html  css  js  c++  java
  • AcWing 1027. 方格取数

    题目传送门

    一、走两次行不行

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

    不过如果先取一次最大路径、抹掉数字后再取一次最大路径(即两次\(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;
    }
    
  • 相关阅读:
    MySQL-InnoDB锁(一)
    Java方法调用机制
    并发编程-锁相关的内存语义
    Java开发工具汇总
    并发编程-底层实现原理
    并发编程-Java内存模型
    JsonTest
    PTA(Basic Level)1034.有理数四则运算
    PTA(Advanced Level)1081.Rational Sum
    PTA(Advanced Level)1008.Elevator
  • 原文地址:https://www.cnblogs.com/littlehb/p/15637264.html
Copyright © 2011-2022 走看看