zoukankan      html  css  js  c++  java
  • HUT1673 传纸条

    题义是两个人分别从左上角和右下角互相传递纸条,但是可以将问题转化为同时在左上角向下传递的两个纸条,求这两个不相交的路径的最大权值。

    TLE了,但总算是把思维的DP弄懂了,总是在路线重复上觉得这个dp方程存在问题,后面才恍悟,在两个纸团坐标相同的点的dp值总是为零的,因为它从头到尾都没有更新。

    定义的dp方程f[i][j][k][p]是指在1,1这个点同时抛的两个纸团的具体坐标(i,j)和(k,p),方程保证不更新两个点相同的值,方程是这样的:

    f[i,j,k,p]:=max{f[i-1,j,k-1,p]
                        f[i-1,j,k,p-1]
                        f[i,j-1,k-1,p] 
                        f[i,j-1,k,p-1] 
             } + G[i,j] + G[k,p] 

    网上有个三维优化的。。。

    代码如下:

    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #define MAXN 55
    using namespace std;
      
    int N, M, dp[MAXN][MAXN][MAXN][MAXN];
      
    int G[MAXN][MAXN];
      
    inline bool judge(int i, int j, int k, int p)
    {
        if (i==k && j==p) {
            if (i==N && j==M) {
                return true;
            }
            else
                return false;
        }   
        else
            return true;
    }
      
    inline int Max(int i, int j, int k, int p)
    {
        int mm = dp[i-1][j][k-1][p];
        mm = max(mm, dp[i-1][j][k][p-1]);
        mm = max(mm, dp[i][j-1][k-1][p]);
        mm = max(mm, dp[i][j-1][k][p-1]);
        return mm;
    }
      
    void DP()
    {
        memset(dp, 0, sizeof (dp));
        for (int i = 1; i <= N; ++i) {
            for (int j = 1; j <= M; ++j) {
                for (int k = 1; k <= N; ++k) {
                    for (int p = 1; p <= M; ++p) {
                        if (judge(i, j, k, p)) {
                        //  puts("JKLJL");
                            dp[i][j][k][p] = Max(i, j, k ,p) + G[i][j] + G[k][p];
                        }
                    }
                }
            }
        }   
        printf("%d\n", dp[N][M][N][M]);
    }
      
    int main()
    { 
        while (scanf("%d %d", &N, &M) == 2) {
            for (int i = 1; i <= N; ++i) {
                for (int j = 1; j <= M; ++j) {
                    scanf("%d", &G[i][j]);
                }
            }
            DP();
        } 
    }

    以下是三维的DP思路,由于从左上角走到右下角只有唯一的步数可能(step = N+M-2),只要知道走了多少步并且所在行数就能够推出所在的列数,因此,状态由四维变成了三维,f[s][i][j], s表示总共走了多少步,i表示第一个纸条所在的行数,j表示第二个纸条所在的行数,由前面已知有步数和行数便能够得出列数,所以dp方程和前面的是差不多的。

    f[s][i][j] = max {

        f[s-1][i][j],      // 表示均从左边传递过来的纸条

        f[s-1][i-1][j],   // 表示第一个纸条从上面传递过来,第二个纸条从左边传递

        f[s-1][i][j-1],   // 表示第一个纸条从左边传递过来,第二个纸条从上面传递

        f[s-1][i-1][j-1]   // 表示均从上面传递过来

    }

    代码如下:

    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define MAXN 55
    using namespace std;
     
    int N, M, G[MAXN][MAXN];
    int dp[105][MAXN][MAXN];
     
    // 定义了三维数组,f[s][i][j] s表示步数,i表示第一个纸团到达的行,j表示第二个纸团到达的行
     
    inline bool judge(int s, int i, int j)
    {
        int y1 = s - i + 2;
        int y2 = s - j + 2; // 这里求出了两个纸团所在位置的纵坐标
        if (y1 > M || y2 > M || y1 < 1 || y2 < 1) {
            return false;
        }
        if (i == j) {
            if (s == N + M - 2 && i == N) {
                return true;
            }
            else { 
                return false;
            }
        }
        else {
            return true;
        }
    }
     
    inline int Max(int s, int i, int j)
    {
        int mm = dp[s-1][i][j];
        mm = max(mm, dp[s-1][i-1][j]);
        mm = max(mm, dp[s-1][i][j-1]);
        mm = max(mm, dp[s-1][i-1][j-1]);
        return mm;
    }
     
    void DP()
    {
        int step = N + M - 2;
        memset(dp, 0, sizeof (dp));
        dp[0][1][1] = 2 * G[1][1];
        for (int s = 1; s <= step; ++s) { // 总共走step步
            for (int i = 1; i <= N; ++i) {
                for (int j = 1; j <= N; ++j) {
                    if (judge(s, i, j)) {
                        dp[s][i][j] = Max(s, i, j)+G[i][s-i+2]+G[j][s-j+2];
                    }
                }
            }
        }
        printf("%d\n", dp[step][N][N]);
    }
     
    int main()
    {
        while (scanf("%d %d", &N, &M) == 2) {
            for (int i = 1; i <= N; ++i) {
                for (int j = 1; j <= M; ++j) {
                    scanf("%d", &G[i][j]);
                }
            }
            DP();
        }
        return 0;
    }
  • 相关阅读:
    Effective Java 第三版——26. 不要使用原始类型
    Effective Java 第三版——25. 将源文件限制为单个顶级类
    Effective Java 第三版——24. 优先考虑静态成员类
    Effective Java 第三版——23. 优先使用类层次而不是标签类
    Effective Java 第三版——22. 接口仅用来定义类型
    Effective Java 第三版——21. 为后代设计接口
    Effective Java 第三版——20. 接口优于抽象类
    Effective Java 第三版——19. 如果使用继承则设计,并文档说明,否则不该使用
    Effective Java 第三版——18. 组合优于继承
    Effective Java 第三版——17. 最小化可变性
  • 原文地址:https://www.cnblogs.com/Lyush/p/2538900.html
Copyright © 2011-2022 走看看