zoukankan      html  css  js  c++  java
  • 【洛谷】P1006 传纸条

    题目链接(小教室,大教室的题目在最后)

    题目描述

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

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

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

    输入输出格式

    输入格式:

    输入文件,第一行有 2 个用空格隔开的整数 m 和 n ,表示班里有 m 行 n 列。

    接下来的 m 行是一个 m×n 的矩阵,矩阵中第 i 行 j 列的整数表示坐在第 i 行 j 列的学生的好心程度。每行的 n 个整数之间用空格隔开。

    输出格式:

    输出文件共一行,包含一个整数,表示来回 2 条路上参与传递纸条的学生的好心程度之和的最大值。

    输入输出样例

    输入样例#1: 复制

    3 3
    0 3 9
    2 8 5
    5 7 0
    

    输出样例#1: 复制

    34

    可以将问题等价于两张纸条同时从左上角传出,只能向右或向下传,由于每个同学只会帮传一次,所以这两张纸条的路线不能相交。

    https://blog.csdn.net/qq_40889820/article/details/81432672可知,可以用dp[i][j][k][l]来表示第一张纸条从map[1][1]传到map[i][j]和第二张纸条从map[1][1]传到map[k][l]的好心程度之和的最大值。显然最后只能一张纸条由map[m][n-1]传到map[m][n],另一张由map[m-1][n]传到map[m][n]。也可知一开始一张纸条向下传,另一张向右传。我们假设第一张纸条是向下传的,第二张纸条是向右传的,为了避免两条路线相交,在循环的时候控制一下就好了,要么从纵坐标入手:l>j,要么从横坐标入手:k<i (原因后面解释)。

    那么最终答案就是dp[m][n-1][m-1][n],

    状态转移方程是dp[i][j][k][l]=max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1],dp[i][j-1][k-1][l],dp[i][j-1][k][l-1])+map[i][j]+map[k][l]。

    那么可以这样

    for(int i=2;i<=m;i++)
    	{
    		for(int j=1;j<=n-1;j++)
    		{
    			for(int k=1;k<i;k++)//注意这里
    			{
    				for(int l=2;l<=n;l++)
    				dp[i][j][k][l]=max(
    				max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1]),
    				max(dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]))
    				+map[i][j]+map[k][l];
    			}
    		}
    	}

    也可以是这样

    for(int i=2;i<=m;i++)
    	{
    		for(int j=1;j<=n-1;j++)
    		{
    			for(int k=1;k<=m-1;k++)
    			{
    				for(int l=j+1;l<=n;l++)//注意这里
    				dp[i][j][k][l]=max(
    				max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1]),
    				max(dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]))
    				+map[i][j]+map[k][l];
    			}
    		}
    	}

    四维 dpAC代码:

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<iomanip>
     5 #include<vector>
     6 #include<cmath>
     7 #include<stack>
     8 using namespace std;
     9 int dp[51][51][51][51];
    10 int map[51][51];//好心程度
    11 int main()
    12 {
    13     int m,n;
    14     cin>>m>>n;
    15     for(int i=1;i<=m;i++)
    16     for(int j=1;j<=n;j++)
    17     cin>>map[i][j];
    18     
    19     memset(dp,0,sizeof(dp));
    20     for(int i=2;i<=m;i++)
    21     {
    22         for(int j=1;j<=n-1;j++)
    23         {
    24             for(int k=1;k<=m-1;k++)
    25             {
    26                 for(int l=j+1;l<=n;l++)
    27                 dp[i][j][k][l]=max(
    28                 max(dp[i-1][j][k-1][l],dp[i-1][j][k][l-1]),
    29                 max(dp[i][j-1][k-1][l],dp[i][j-1][k][l-1]))
    30                 +map[i][j]+map[k][l];
    31             }
    32         }
    33     }
    34     cout<<dp[m][n-1][m-1][n];
    35 }
    View Code

    现在我们来考虑优化,分析一下纸条传递的规律,发现他们向下和向右走的总步数是相等的(这下上面留下的问题就解决啦!总步数相同的前提下,同列必定同行,同行必定同列,即重合),map[1][1]传到map[i][j]的总步数是i-1+j-1=i+j-2,即第一张纸条从map[1][1]传到map[m][n-1]总的步数是m-1+n-1-1=m+n-3,同理,第二张纸条总的步数也是如此,那么只要知道走的总步数和向下(右)走的步数,向右(下)的步数便可通过计算得出。

    用三维dp[s][i][j]表示走的总步数为s,第一张纸条向下走了i步由map[1][1]传到了map[i+1][s+1-i],第二张纸条向下走了j步由map[1][1]传到了map[j+1][s+1-j]。

    那么最终答案等于dp[m+n-3][m-1][m-2]。

    状态转移方程是dp[s][i][j]=max(dp[s-1][i-1][j],dp[s-1][i][j-1],dp[s-1][i][j],dp[s-1][i-1][j-1])+map[i+1][s+1-i]+map[j+1][s+1-j]

    三维dp AC代码:

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<string>
     4 #include<iomanip>
     5 #include<vector>
     6 #include<cmath>
     7 #include<stack>
     8 using namespace std;
     9 int dp[102][51][51];
    10 int map[51][51];
    11 int main()
    12 {
    13     int m,n;
    14     cin>>m>>n;
    15     for(int i=1;i<=m;i++)
    16     for(int j=1;j<=n;j++)
    17     cin>>map[i][j];
    18     
    19     for(int s=1;s<=m+n-3;s++)
    20     {
    21         for(int i=1;i<=m-1;i++)
    22         {
    23                     //j=0的时候下标出现了负数,经实验,默认为0,不过查了一下貌似挺危险,不管,过了就是过了 
    24             for(int j=0;j<i;j++)
    25             dp[s][i][j]=max(
    26             max(dp[s-1][i][j],dp[s-1][i-1][j-1]),
    27             max(dp[s-1][i][j-1],dp[s-1][i-1][j]))
    28             +map[i+1][s+1-i]+map[j+1][s+1-j];
    29         }
    30     }
    31     cout<<dp[m+n-3][m-1][m-2];
    32 }
    View Code

    还可不可以再优化呢?当然!用滚动数组试一下( ´-ω ・)▄︻┻┳══━一

    不过要注意的是i和j的枚举要倒叙,防止状态在转移之前被修改

    二维dp AC代码:

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<string>
     4 #include<iomanip>
     5 #include<vector>
     6 #include<cmath>
     7 #include<stack>
     8 using namespace std;
     9 int dp[210][210];
    10 int map[210][210];
    11 int main()
    12 {
    13     int m,n;
    14     cin>>m>>n;
    15     for(int i=1;i<=m;i++)
    16     for(int j=1;j<=n;j++)
    17     cin>>map[i][j];
    18     
    19     for(int s=1;s<=m+n-3;s++)
    20     {
    21         for(int i=min(m-1,s);i>=1;i--)//最开始的步数要判断一下
    22         {
    23             for(int j=i-1;j>=0;j--)//和i都要倒叙枚举
    24             dp[i][j]=max(
    25             max(dp[i][j],dp[i-1][j-1]),
    26             max(dp[i][j-1],dp[i-1][j]))
    27             +map[i+1][s+1-i]+map[j+1][s+1-j];
    28         }
    29     }
    30     cout<<dp[m-1][m-2];
    31 }
    View Code

    大教室传纸条(数据规模扩大,空间限制变小)

  • 相关阅读:
    17。3.12---re模块--正则表达式操作指南
    17.3.12---还有未整理的模块
    17.3.12---xmlrpclib模块
    17.3.12----math模块
    17.3.12--smtplib模块发送邮件__抄送,安装与下载
    17.3.12---logging日志模块level配置操作
    17.3.12---urlparse模块的URL下载
    17.3.12---socket
    17.3.12--urllib2模块
    SQLSERVER 中的共用表达式(CTE)
  • 原文地址:https://www.cnblogs.com/wangzhebufangqi/p/12796185.html
Copyright © 2011-2022 走看看