1018. 最低通行费


题目传送门

一、题目大意

从左上角进去,从右下角出来,每穿过一个小方格,就要消耗一单位的时间。
商人必须要在\(2*n-1\)的时间内,穿到右下角去。每经过一个小方格,都需要缴纳一定的费用,问我们总花费最小是多少?

二、题目分析

摘花生的题与这道题特别相似,摘花生是从左上走到右下角,只能是向下或者向右走,不能回头走,问我们摘到的最大值是多少?

这道题要求在\(2*n-1\)的时间内,从左上角走到右下角,最小花费是多少?

理解一下这个\(2*n-1\),如果我们不走回头路的话,那么需要走\(2*n-1\)步,所以消耗的时间为\(2*n-1\)单位时间。

换句话说,就是只能向右或向下走,不能向左或向上,否则不能在\(2*n-1\)步中走到右下角。

分析完\(2*n-1\),就明白这道题其实就是摘花生那道题,只不过不是求最大值,而是求最小值即可。

线性结构就是\(f[i]\),网络图就是\(f[i][j]\),如果是背包问题,那么第一维是物品,第二维是体积。

三、特别之处

1、注意边界

每一个位置,都可以从它的正上方或者左前方过来,但第一行不能从上一行过来,第一列不能从上一列过来,需要特殊处理。

2、最小值需要注意初始化

数组声明在全局区,默认值是\(0\),而求最小值,而可能把\(0\)认为小于已经取得的最小值,导致最终结果为\(0\)的错误,所以,需要对数据进行最大化的初始化,预求最小,先设最大!

四、实现代码

#include 

using namespace std;

const int N = 110;
const int INF = 0x3f3f3f3f;
int n;
int w[N][N];
int f[N][N];

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 = 1; i <= n; i++)f[1][i] = f[1][i - 1] + w[1][i];

    //初始化第一列,因为第一列的每个位置,只能从上侧过来,所以有下面的递推式。
    for (int i = 1; i <= n; i++)f[i][1] = f[i - 1][1] + w[i][1];

    //第一行第一列讨论完,剩下的可以愉快的递推了~
    for (int i = 2; i <= n; i++)
        for (int j = 2; j <= n; j++) {
            //初始化数据,预求最小,先设最大
            f[i][j] = INF;
            f[i][j] = min(f[i][j - 1], f[i - 1][j]) + w[i][j];
        }
    //输出大吉
    printf("%d \n", f[n][n]);
    return 0;
}