问题描述
刷微博,编程序。如下图所示,@北京发布 提出了如下“头脑震荡”问题。对此问题做一般化描述:
有 n 阶方阵,从矩阵的左下角元素为起点,从行或列(水平或垂直)两个方向上移动,直到右上角。
求出有多少条路径可以使得经过的元素累加值最大,最大值是多少。
输入格式
第一行整数 n,表示矩阵的阶数
第二行起,每行 n 个整数,以空格分隔,共 n 行。
输出格式
一行,两个空格分隔的数,第一个表示最大值路径的条数,第二个表示最大值。
样例输入
5
4 5 4 5 6
2 6 5 4 6
2 6 6 5 2
4 5 2 2 5
5 2 5 6 4
样例输出
3 47
数据范围
2 ≤ n ≤ 10
题解:
线性DP & DFS:
f[i][j]
:
集合
:所有从 (n, 1) 走到 (i, j) 的方案的集合属性
:最大值
#include <iostream>
using namespace std;
const int N = 15;
int n, cnt = 1; // 初始化:至少有一条最大值路径
int w[N][N], f[N][N];
int dfs(int x, int y) // 从右上角开始搜索
{
if(x == n && y == 1) return 0; // 找到左下角,退出
if(x > n || y < 1) return 0; // 出界,退出
if(f[x + 1][y] < f[x][y - 1]) dfs(x, y - 1);
if(f[x][y - 1] < f[x + 1][y]) dfs(x + 1, y);
if(f[x][y - 1] == f[x + 1][y]) // 两点的路径之和相同
{
cnt ++; // 路径数量 + 1
dfs(x, y - 1);
dfs(x + 1, y);
}
return cnt;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++)
cin >> w[i][j];
for (int i = n; i >= 1; i --) // 由于起点为左下角,所以要逆序枚举
for (int j = 1; j <= n; j ++)
{
if(i == n && j == 1) f[i][j] = w[i][j]; // 起点的值就是本身
else if(i == n) f[i][j] = f[i][j - 1] + w[i][j]; // 第 n行 = 左边的值 + 本身
else if(j == 1) f[i][j] = f[i + 1][j] + w[i][j]; // 第一列 = 下面的值 + 本身
else f[i][j] = max(f[i + 1][j], f[i][j - 1]) + w[i][j]; // 其余情况:在两点中选择路径之和最大的 + 本身
}
cout << dfs(1, n) << " " << f[1][n] << endl;
return 0;
}