1018.最低通行费
一个商人穿过一个 $N×N$ 的正方形的网格,去参加一个非常重要的商务活动。
他要从网格的左上角进,右下角出.
每穿越中间 $1$ 个小方格,都要花费 $1$ 个单位时间。
商人必须在 $(2N-1)$ 个单位时间穿越出去。
而在经过中间的每个小方格时,都需要缴纳一定的费用。
这个商人期望在规定时间内用最少费用穿越出去。
请问至少需要多少费用?
注意:不能对角穿越各个小方格(即,只能向上下左右四个方向移动且不能离开网格)。
输入格式
第一行是一个整数,表示正方形的宽度 $N$。
后面 $N$ 行,每行 $N$ 个不大于 $100$ 的正整数,为网格上每个小方格的费用。
输出格式
输出一个整数,表示至少需要的费用。
数据范围
$1 \le N \le 100$
观察题意,发现就是DP
可以参考这一篇题解的动规思路 1015.摘花生
直接写dp方程:
状态表示
$f[i][j]$ 表示从左下角到位置 $[i, j]$ 的最小值
状态转移
很明显,枚举顺序是先行后纵
所以,$[i, j]$ (只能)依赖于 $[i - 1, j]$ 和 $[i, j - 1]$
也就是,f[i][j] = max(f[i - 1][j], f[i][j - 1]) + w;
而第 i 层的答案只依赖于第 i 层和第 i - 1 层,容易想到滚动数组优化
在看到方程,发现不用滚动数组,直接用一维存即可(具体解释见代码)
一维转移:f[j] = max(f[j], f[j - 1]) + w;
答案表示
用二维存就是 f[n][m]
用一维存就是 f[m]
注意这道题是求最小值,所以要注意边界条件
c++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 109;
int n; //正方形的宽度
int f[N]; //f[i][j] 表示从坐标 [1, 1] 到坐标 [i, j] 的最小值
int main()
{
cin >> n; //输入宽度
memset(f, 0x3f, sizeof f); //注意边界条件
for(int i = 1;i <= n;++ i)
for(int j = 1, w;j <= n;++ j)
//枚举 i 和 j,注意顺序不能换
{
cin >> w; //这里是省空间,w 就是 w[i][j];
if(i == 1 && j == 1) f[j] = w; //注意当 i == 1 && j == 1 时,无法转移,所以要特判。
else f[j] = min(f[j], f[j - 1]) + w; //这里是优化,第 i 层只依赖于第 i 层和第 i - 1 层
//所以可以直接优化空间,这里的 f[j - 1] 对应的是
//f[i][j - 1],而 f[j] 对应的是 f[i - 1][j]
}
cout << f[n] << endl; //输出
return 0;
}