题目描述
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。
一次素质拓展活动中,班上同学安排坐成一个 m
行 n
列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。
幸运的是,他们可以通过传纸条来进行交流。
纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标 (1,1)
,小轩坐在矩阵的右下角,坐标 (m,n)
。
从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。
班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙,反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用 0
表示),可以用一个 0∼100
的自然数来表示,数越大表示越好心。
小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。
现在,请你帮助小渊和小轩找到这样的两条路径。
输入格式
第一行有 2
个用空格隔开的整数 m
和 n
,表示学生矩阵有 m
行 n
列。
接下来的 m
行是一个 m×n
的矩阵,矩阵中第 i
行 j
列的整数表示坐在第 i
行 j
列的学生的好心程度,每行的 n
个整数之间用空格隔开。
输出格式
输出一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
数据范围
1≤n,m≤50
样例
输入样例:
3 3
0 3 9
2 8 5
5 7 0
输出样例:
34
算法1
动态规划(蓝书上的思路)
设i表示当前的已经移动的步数(0表示还没移动过),最后一共需要移动n+m-2步,所以以步数为状态阶段,构造状态数组f[i][x1][x2],表示当前移动了i步时,一个处于x1行,一个处于x2行的最大值。
拿当前状态更新下一个转态f[i+1]时的状态,此时x1,x2在下一步各有两种传递方向,所以分别枚举,构造出新的行号nx1,nx2,再根据i+2=x1+y1公式推出对应的y坐标。例如:i步时,一个处于x1行,则列好为i+2-x1;当i+1步时,处于x1行的传递到了新的行号nx1,则此时ny1=i+1+2-nx1。其他类似。
初始状态:
f[0][1][1]=v[1][1];
目标状态
f[n+m-2][n][n]
C++ 代码
#include <iostream>
using namespace std;
const int maxn = 55;
int n, m;
int v[maxn][maxn];
int f[2*maxn][maxn][maxn] = {0};
//纸条传递方向
int X1[4] = { 1, 1, 0, 0 };
int X2[4] = { 1, 0, 1, 0 };
int cal() {
f[0][1][1] = v[1][1];
for (int i = 0; i < m + n - 2; i++) {//当前移动的步数(0表示还没移动过)
for (int x1 = max(1, i-(m-1)+1); x1 <= min(i + 1, n); x1++) {//枚举当前x1的行号,i-(m-1)表示当前i步的前提下行号至少需要走的步数
for (int x2 = max(1, i - (m - 1) + 1); x2 <= min(i + 1, n); x2++) {//枚举当前x2的行号
if (i!=0 && x1 == x2)continue;//当前的两个位置不能重合(除了初始位置)
for (int d = 0; d < 4; d++) {//枚举4个传递方向
int nx1 = x1 + X1[d], nx2 = x2 + X2[d];
if (nx1 > n || nx2 > n)continue;
int ny1 = i + 3 - nx1, ny2 = i + 3 - nx2;
if (ny1 > m || ny2 > m)continue;
if ((i + 1 == m + n - 2) || nx1 != nx2) {//如果中间状态则不能在相同格子,除了最后一次
f[i + 1][nx1][nx2] = max(f[i + 1][nx1][nx2], f[i][x1][x2] + v[nx1][ny1] + v[nx2][ny2]);
}
}
}
}
}
return f[n + m - 2][n][n];
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%d", &v[i][j]);
}
}
int res = cal();
printf("%d\n", res);
return 0;
}