• 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;
    }
  • 相关阅读:
    openSUSE字体美化
    [转摘]关于创建oracle dblink 过程的几点心得
    IList及泛型集合类转换DataTable
    C# 编码规范和编程好习惯
    随机数和随机字符串
    ThrowActivity 光阴的故事
    数据库的数据 转化为XML 在页面上浏览 光阴的故事
    EventHandlingScopeActivity 光阴的故事
    workflow 角色的使用关键 光阴的故事
    ConditionedActivityGroup 光阴的故事
  • 原文地址:https://www.cnblogs.com/Lyush/p/2538900.html
Copyright © 2020-2023  润新知