开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 31 天,点击查看活动详情
题目描述
班上同学安排坐成一个 m 行 n 列的矩阵,小渊和小轩被安排在矩阵对角线的两端,两人通过传纸条交流, 纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标 (1,1),小轩坐在矩阵的右下角,坐标 (m,n)。
从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递,班里每个同学都可以帮他们传递,但只会帮他们一次,全班每个同学愿意帮忙的好感度有高有低,数越大表示越好心。
小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。
现在,请你帮助小渊和小轩找到这样的两条路径。
输入格式
第一行有 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
题目分析
首先经过分析可知一次来回的路径,相当于从左上向右下连续走两次。
定义 表示两次同时走了 步,第一次在 处,第二次在 处的所有走法的最大值。
经过模拟可以发现出现路径交集的格子一定在两次路径的相同步数处。因此可以让两次路径同时从起点出发,每次同时走一步,这样路径中若存在相交的格子则一定在同一时间的同一步内。
考虑状态的转移:按照最后一步两次不同的走法分成四种情况:
同时向右走,最大值是 f[k - 1, i, j] + w(k, i, j);
一个向右走,一个向下走,最大分是 f[k - 1, i, j - 1] + w(k, i, j);
一个向下走,一个向右走,最大值是 f[k - 1, i - 1, j] + w(k, i, j);
同时向下走,最大值是 f[k - 1, i - 1, j - 1] + w(k, i, j);
注意 和 不能相等。
Accept代码
#include <bits/stdc++.h>
using namespace std;
const int N = 55;
int g[N][N], f[N * 2][N][N];
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
cin >> g[i][j];
for (int k = 2; k <= n + m; k ++)
for (int i1 = 1; i1 <= n; i1 ++)
for (int i2 = 1; i2 <= n; i2 ++)
{
int j1 = k - i1, j2 = k - i2;
if (j1 >= 1 && j1 <= m && j2 >= 1 && j2 <= m)
{
int v = g[i1][j1] + (i1 == i2 ? 0 : g[i2][j2]);
int &x = f[k][i1][i2];
x = max(x, f[k - 1][i1 - 1][i2]);
x = max(x, f[k - 1][i1][i2 - 1]);
x = max(x, f[k - 1][i1 - 1][i2 - 1]);
x = max(x, f[k - 1][i1][i2]);
x += v;
}
}
cout << f[n + m][n][n];
return 0;
}