动态规划之传纸条(多线程dp)

来源:互联网 发布:mac上打开flac 编辑:程序博客网 时间:2024/05/19 19:42

目录

  1. 传纸条问题简介
  2. 举例以及详细分析
  3. 代码块
  4. 测试结果

传纸条问题简介

题目描述:
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个m行n列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。幸运的是,他们可以通过传纸条来进行交流。纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙。反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度只和最大。现在,请你帮助小渊和小轩找到这样的两条路径。
输入格式:
输入第一行有2个用空格隔开的整数m和n,表示班里有m行n列(1<=m,n<=50)。
接下来的m行是一个m*n的矩阵,矩阵中第i行j列的整数表示坐在第i行j列的学生的好心程度。每行的n个整数之间用空格隔开。
输出格式:
输出共一行,包含一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
输入样例:
3 3
0 3 9
2 8 5
5 7 0
输出样例:
34


举例及其详解

因为多线程dp问题的a[i][0]和a[0][j]必须为0,所以初始点从a[1][1]开始。
题目要求从1,1到m,n两条不相交的路径(因为每个同学最多只帮一次忙,所以两条路径不会相交)的权值和最大值。我们可以把问题转化为有两个人同时从1,1向m,n走,两人不相交。由于两人可以走所有的格子,我们分别用,x1,y1,x2,y2来表示某一时刻两人的位置。所以我们要用一个四维数组来表示出所有的状态。
这里写图片描述

如图所示:由于只能向下或者向右走,所以(x1,y1),(x2,y2)分别可以有图中黄色部分走一步得到,从上一步到这一步总共有4种情况。所以状态转移方程为:(a[][]存权值)
dp[x1][y1][x2][y2]=max(dp[x1][y1-1][x2][y2-1],dp[x1][y1-1][x2-1][y2],dp[x1-1][y1][x2][y2-1],dp[x1-1][y1][x2-1][y2])+a[x1][y1]+a[x2][y2];

又因为,每个同学最多只能帮忙一次,所以当两人走到同一点时,只能给一个人加这个点的权值,代码可以这么来写:
这里写图片描述

时间复杂度为O(n^4);
优化:
这里写图片描述
从(1,1)出发走恰好需要5步能到达的点
他们都在一条45度的斜线上,且满足x+y=7。
更普遍的,对于所有步数为s可以到达的坐标,x+y=s+2。所以,我们可以用走的步数来划分阶段,通过不同的步数,把这个题目划分成若干个不同的阶段。最后一个阶段有且仅有一个点:(n,m)。
通过简化假设所走步数k,k=x1+y1=x2+y2
优化方案有两种:

  1. 可知x1+y1=x2+y2,所以y2=x1+y1-x2;因此我们可以把dp[x1][y1][x2][y2]压缩成dp[x1][y1][x2],最后的y2通过可以得出,能够使时间复杂度将为O(n^3);
  2. 我们令k表示走过的步数,所以有x1+y1=k,x2+y2=k(初始时k=1+1);知道了k,x1,x2,那么我们就可以知道y1,y2了。所以状态可以降低为3维,状态转移方程:
    dp[k][x1][x2]=max(dp[k-1][x1-1][x2],dp[k-1][x1-1][x2-1],dp[k-1][x1][x2],dp[k-1][x1][x2-1])+a[x1][k-x1]+a[x2][k-x2];

优化方案1的代码块

#include<stdio.h>#include<stdlib.h>int a[55][55];int dp[55][55][55][55];//dp[x1][y1][x2][y2]表示第一个传到的地方(x1,y1)和第二个人传到的地方(x2,y2)的路径的和;int Max(int a,int b,int c,int d){    return max(max(a, b), max(c, d));}int Fun(int m,int n){    int x1, y1, x2, y2;    for ( x1 = 1; x1 <= m; x1++)    {        for ( y1 = 1; y1 <= n; y1++)        {            for ( x2 = 1; x2 <= m; x2++)            {                if (x1 + y1 - x2 > 0)                {                     y2 = x1 + y1 - x2;                }                else                {                    continue;                }                dp[x1][y1][x2][y2] = Max(dp[x1-1][y1][x2-1][y2], dp[x1][y1-1][x2][y2-1], dp[x1-1][y1][x2][y2-1], dp[x1][y1-1][x2-1][y2])+a[x1][y1]+a[x2][y2];                if (x1 == x2&&y1 == y2)                {                    dp[x1][y1][x2][y2] -= a[x1][y1];//当x1==x2时,说明两个人相交了,因为每个同学只能传递一次,所以这里应该减去一个人在此的路程                }            }        }    }    return dp[m][n][m][n];}int main(void){    int t;    printf("请输入有几次题目\n");    scanf("%d", &t);    while (t--)    {        memset(dp, 0, sizeof(dp));        printf("请输入传纸条所在的方阵的行和列:\n");        int m, n;        scanf("%d%d", &m, &n);        for (int i = 1; i <= m; i++)//因为多线程dp问题的a[i][0]和a[0][i]必须为0,所以i,j的下标从1开始        {            for (int j = 1; j <= n; j++)            {                scanf("%d", &a[i][j]);            }        }        printf("传纸条的路数之和:\n");        printf("%d\n", Fun(m,n));    }    system("pause");    return 0; }

方案1测试结果

这里写图片描述


优化方案2的代码块

#include <iostream>#include <cstdio>#include <cstring>#include <cmath>using namespace std;int const N = 55;int a[N][N];int dp[2*N][N][N];int m,n;int max_4(int a,int b,int c,int d){    return max(max(a,b),max(c,d));}int max_path(){    for(int k=2;k<=n+m-1;k++)//开始时x1+y1=2 ,结束时x1+y1=n+m-1;    {        for(int x1=1;x1<=m;x1++)        {            for(int x2=1;x2<=m;x2++)            {                if(x1==x2) continue;//注意                dp[k][x1][x2]=max_4(dp[k-1][x1][x2],dp[k-1][x1-1][x2],dp[k-1][x1][x2-1],dp[k-1][x1-1][x2-1])+a[x1][k-x1]+a[x2][k-x2];            }        }    }    return dp[m+n-1][m][m-1];}int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%d%d",&m,&n);        memset(dp,0,sizeof(dp));        for(int i=1;i<=m;i++)        {            for(int j=1;j<=n;j++)            {                scanf("%d",&a[i][j]);            }        }        printf("%d\n",max_path());    }    return 0;}

方案2测试结果

这里写图片描述

0 0
原创粉丝点击