• pyoj61 双线DP


    传纸条(一)

    时间限制:2000 ms  |  内存限制:65535 KB
    难度:5
     
    描述

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

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


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

     
    输入
    第一行输入N(0<N<100)表示待测数据组数。
    每组测试数据输入的第一行有2个用空格隔开的整数m和n,表示班里有m行n列(2<=m,n<=50)。 
    接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度(不大于1000)。每行的n个整数之间用空格隔开。
    输出
    每组测试数据输出共一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。 
    样例输入
    1
    3 3
    0 3 9
    2 8 5
    5 7 0
    样例输出
    34
    妈蛋,看zzuli那个不会过来的
    要同时考虑两个人的走动情况同时DP,假设两个人坐标为(i,j).(p.q)
    直观DP方程:
     dp[i][j][p][q]=max(dp[i-1][j][p-1][q],dp[i-1][j][p][q-1],dp[i][j-1][p-1][q],dp[i][j-1][p][q-1])+e[i][j]+e[p][q];
    四维必爆无疑,考虑压缩为三维,不难发现类似于在0的个数那道题的发现,在只能往右和下走的情况的下,只能走n+m-2步才能到达
    又同时从(1,1)出发,每次都走一步的话,不难得到:i+j==p+q==k,则(k-2)表示当前走了几步
    所以我们只要考虑出k和两个坐标的一个维就可以知道另一维的值
    所以考虑DP方程:dp[k][x1][x2]=max{dp[k-1][x1][x2],dp[k-1][x1-1][x2],dp[k-1][x1][x2-1],dp[k-1][x1-1][x2-1]}+e[x1][k+2-x1]+e[x2][k+2-x2];
    x1,x2分别表示两个点的横坐标,根据k求出纵坐标
     
    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define Max(a,b,c,d) max(max(a,b),max(c,d))
    #define ql(a) memset(a,0,sizeof(a))
    #define CIN(a) scanf("%d",&a)
    int dp[105][61][61];
    int e[61][61];
    int main()
    {
    int x1,y1,x2,y2;
    int n,m,i,j,k,t;
    //cin>>t;
    CIN(t);
    while(t--){ql(e),ql(dp);
    cin>>n>>m;
    for(i=1;i<=n;++i)
    for(j=1;j<=m;++j) CIN(e[i][j]);
    for(k=1;k<n+m-2;++k)
    for(x1=1;x1<=n;++x1)
    for(x2=x1+1;x2<=n;++x2)     //红色部分的优化意义:打印出结果不难发现结果成对角线对称,所以不必重复计算
    if(x1==x2||k+2-x1<0||k+2-x2<0||k+2-x1>m||k+2-x2>m) continue;
    else{
    dp[k][x1][x2]=Max(dp[k-1][x1][x2],dp[k-1][x1][x2-1],dp[k-1][x1-1][x2],dp[k-1][x1-1][x2-1])
    +e[x1][k+2-x1]+e[x2][k+2-x2];
    }
    for(k=1;k<n+m-2;k++){
    for(i=1;i<=n;++i){
    for(j=1;j<=n;++j)
    cout<<dp[k][i][j]<<" ";
    cout<<endl;
    }
    cout<<endl;
    }
    printf("%d ",max(dp[k-1][n-1][n],dp[k-1][n][n-1]));   //其实不必比较,二者是等价的
    }
    return 0;
    }

    之所以会产生对角线成对称结果的原因:
    md表达能力太差,举个栗子,假设两条线路L1,L2,

    当L1走到(x1,k+2-x1),L2走到(x2,k+2-x2)获得的值与L1走到(x2,k+2-x2),L2走到(x1,k+2-x1)的值实际上是一样的!

    想象一下L1,L2走过的一对最佳路线对调之后L1->L2,L2->L1这样不又出现一种结果吗!但是显然这两种结果的值肯定是一样的,所以根据这个去重!

    也就是说,当选择x2从一开始之后,最后输出时只需要从终点格子的旁边的两格随便选一个即可。

    而当选择x2从x1+1开始去重时,最后输出时切记比较一下两个的大小在输出,因为有一个格子没有计算,一旦选错直接WA!!

  • 相关阅读:
    进程与线程的一个简单解释
    如何更优雅的写出你的SQL语句
    SQL 性能优化梳理
    如何写出让同事无法维护的代码?
    Linux配置IP常用命令
    Linux中防火墙命令笔记
    蓝牙技术的工作原理及用途
    别死写代码,这 25 条比涨工资都重要
    搞清这些陷阱,NULL和三值逻辑再也不会作妖
    计算机网络:TCP和UDP的对比
  • 原文地址:https://www.cnblogs.com/zzqc/p/6665348.html
Copyright © 2020-2023  润新知