简单动态规划题

题目简述

n * m 的方格,每一个都有一个能量值,初始在左上角,终点在右下角,初始能量就是起点方格的能量值。每只能向右或向下走一格,一消耗1能量,一可以走若干,只有一走完后才更新能量,中间经过的方格能量无效;

每走一次,能量清零,新的初始能量为当前方格的能量值。求有多少种不同的路径从左上角到达右下角(按顺序依次到达的点只要有一个不同时,就视为不同的路径),答案对10000取余。

算法分析

题面又臭又长,很是唬人,其实就是个简单dp题。 简单来说,本题就是求从 [0][0] 到 [n-1][m-1] 的方案数目, dfs 的递归思想。

记从 [0][0] 到 [x][y] 的方案数目为 dp[x][y],则由回溯思想知:

若能从 [tx][ty] 到达 [x][y] ,则 dp[x][y] 就是所有的 dp[tx][ty] 的累加和。

那我们就简单的从左上往右下更新 dp 数组的值即可。

C++代码

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int No8_dp(vector<vector<int>>& energy) {
        const int mod = 10000;
        int n = energy.size(), m = energy[0].size();
        // dp[i][j]表示从(0,0)到达(i,j)的方案数
        vector<vector<int>>dp(n, vector<int>(m, 0));
        dp[0][0] = 1;
        for (int x = 0; x < n; x++) {
            for (int y = 0; y < m; y++) {
                int cur = energy[x][y];
                int mi = min(cur, n - 1 - x);
                for (int i = 0; i <= mi; i++) {     // 向下走i步
                    int mj = min(cur - i, m - 1 - y);
                    for (int j = 0; j <= mj; j++) {    // 向右走j步
                        if (i == 0 && j == 0)continue;
                        // 能从(x,y)走到的点,其方案数就累加上(x,y)的方案
                        dp[x + i][y + j] = (dp[x + i][y + j] + dp[x][y]) % mod;
                    }
                }
            }
        }
        return dp[n - 1][m - 1];
    }

int main(){
    int T;
    cin>>T;
    while(T--){
        int n,m;
        cin>>n>>m;
        vector<vector<int>>energy(n, vector<int>(m));
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++)
                cin>>energy[i][j];
        }
        cout<<No8_dp(energy)<<'\n';
    }
    return 0;
}