HUT-1673 传纸条

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

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;

}

你可能感兴趣的:(T)