题目链接(小教室,大教室的题目在最后)
题目描述
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。一次素质拓展活动中,班上同学安排做成一个 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 }
现在我们来考虑优化,分析一下纸条传递的规律,发现他们向下和向右走的总步数是相等的(这下上面留下的问题就解决啦!总步数相同的前提下,同列必定同行,同行必定同列,即重合),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 }
还可不可以再优化呢?当然!用滚动数组试一下( ´-ω ・)▄︻┻┳══━一
不过要注意的是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 }
大教室传纸条(数据规模扩大,空间限制变小)