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

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

  • 相关阅读:
    tuple 元组及字典dict
    day 49 css属性补充浮动 属性定位 抽屉作业
    day48 选择器(基本、层级 、属性) css属性
    day47 列表 表单 css初识
    day 46 http和html
    day 45索引
    day 44 练习题讲解 多表查询
    day 40 多表查询 子查询
    day39 表之间的关联关系、 补充 表操作总结 where 、group by、
    day38 数据类型 约束条件
  • 原文地址:https://www.cnblogs.com/wangzhebufangqi/p/12796185.html
Copyright © 2011-2022 走看看