洛谷Oj-传纸条-多线程DP

来源:互联网 发布:java参考文献近五年的 编辑:程序博客网 时间:2024/05/19 19:30

问题描述:
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。

在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。

还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。现在,请你帮助小渊和小轩找到这样的两条路径。
学习资料:
http://blog.csdn.net/u011077606/article/details/43302987
AC代码:

int mat[100][100],dp[60][60][60][60];//dp[i][j][x][y]代表第一个人走到点(i,j)并且第二个人走到点(x,y)所获得好感度的最大值int main(){    int m,n;    //输入    cin >> m >> n;    for(int i = 1; i <= m; ++i)        for(int j = 1; j <= n; ++j)            scanf("%d",&mat[i][j]);    for(int i = 1; i <= m; ++i)        for(int j = 1; j <= n; ++j)            for(int x = 1; x <= m; ++x)                for(int y = 1; y <= n; ++y)//dp[i][j][x][y]可由四个状态转移而来,由于是求最大值所以不担心越界的问题                {                    int t1 = max(dp[i - 1][j][x][y - 1],dp[i][j - 1][x][y - 1]);                    int t2 = max(dp[i - 1][j][x - 1][y],dp[i][j - 1][x - 1][y]);                    dp[i][j][x][y] = max(t1,t2) + mat[i][j] + mat[x][y];//四个状态中的最大值再加上点(i,j)和(x,y)的好感度                    if(i == x && j == y)//如果有交叉点,减去即可                        dp[i][j][x][y] -= mat[i][j];                }    printf("%d\n",dp[m][n][m][n]);//终点    return 0;}

解决方法:
在一开始我想到了先走一次,记录下路径并标记然后再走一次的方法。可是尝试了许多办法,记录路径难度太大。
走一次的代码:

int mat[100][100],dp[110][60][60];//dp[state][i][j]代表走到第state阶段中的点(i,j)所获得好感度的最大值int main(){    int m,n;    //输入    cin >> m >> n;    for(int i = 1; i <= m; ++i)        for(int j = 1; j <= n; ++j)            scanf("%d",&mat[i][j]);    for(int state = 3; state <= m + n; ++state)//将每一条副对角线视为一个阶段        for(int i = 1; i <= m; ++i)            for(int j = 1; j <= n; ++j)                if(i + j == state)//如果属于这个阶段,就转移                {                    if(i - 1 < 1)//越界,但由于界外元素的值为0,故不判断也可以                        dp[state][i][j] = dp[state - 1][i][j - 1] + mat[i][j];                    else if(j - 1 < 1)//越界                        dp[state][i][j] = dp[state - 1][i - 1][j] + mat[i][j];                    else                        dp[state][i][j] = max(dp[state - 1][i][j - 1],dp[state - 1][i - 1][j]) + mat[i][j];//上方的点,左方的点                }    printf("%d\n",dp[m + n][m][n]);//终点    return 0;}

实在是束手无策后,对照题解学习了一下,得知此题也叫做棋盘形DP。
一来一回等价于两次从左上角的点到右下角的点,所以将此问题看做是两张纸条同时向终点传
如果路径交叉,我们可以把它转化成不交叉的等价情形
此时含有交叉点的两条路径一定不是最终的答案,所以计算时只需要将这种情况下的值如实算出来
这道题因为有i + j = x + y的关系,所以还可以优化成三维

原创粉丝点击