【牛客网动态规划专项】DP13 过河卒

题目描述

在这里插入图片描述
在这里插入图片描述

解题思路

棋盘 棋盘可以表示为 m a p [ 0... n ] [ 0... m ] map[0...n][0...m] map[0...n][0...m],要注意本题的棋盘是一个大小为 ( n + 1 ) × ( m + 1 ) (n+1) \times (m+1) (n+1)×(m+1) 的矩阵。

不可抵达位置 棋盘上的不可抵达位置包括:

  • 马所在的点 ( x , y ) (x, y) (x,y)
  • 马的控制点 { ( x 1 , y 1 ) , ∣ x 1 − x ∣ + ∣ y 1 − y ∣ = 3 , x 1 ≠ x , y 1 ≠ y } \{(x_1,y_1), |x_1-x|+|y_1-y|=3, x_1 \neq x, y_1 \neq y\} {(x1,y1),x1x+y1y=3,x1=x,y1=y}

用一个函数 bool reachable(int x1, int y1) 来判断某一个点是否可达:

bool reachable(int x1, int y1){
    if((x1 == x && y1 == y)
    || ((abs(x1-x)+abs(y1-y)==3 && x1!=x && y1!=y))){
        return false;
    }else{
        return true;
    }
}

状态转移方程

备忘录 创建一个和棋盘同样大小的备忘录 d p [ 0... n ] [ 0... m ] dp[0...n][0...m] dp[0...n][0...m],其中 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示从棋盘上的 ( 0 , 0 ) (0,0) (0,0)点到 ( i , j ) (i, j) (i,j)点的路径总数, d p [ n ] [ m ] dp[n][m] dp[n][m] 为原问题答案。

  1. 左上角 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]:如果第一个位置就不可达,那么后面的所有位置都不可达,可以直接返回总路径条数0;如果第一个位置可达,则将 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0] 初始化为1。
if(!reachable(0, 0)){
    // dp[0][0] = 0;
    return 0;
}else{
    dp[0][0] = 1;
}
  1. 最左边一行 d p [ i ] [ 0 ] dp[i][0] dp[i][0]:如果 m a p [ i ] [ 0 ] map[i][0] map[i][0] 是不可达位置, d p [ i ] [ 0 ] dp[i][0] dp[i][0]等于0;如果 m a p [ i ] [ 0 ] map[i][0] map[i][0] 可达,到达它的唯一方式就是从 m a p [ i − 1 ] [ 0 ] map[i-1][0] map[i1][0] 位置向下走一步,因此到达 m a p [ i ] [ 0 ] map[i][0] map[i][0] 的所有路径就是到达 m a p [ i − 1 ] [ 0 ] map[i-1][0] map[i1][0] 的所有路径再加上一步, d p [ i ] [ 0 ] = d p [ i − 1 ] [ 0 ] dp[i][0] = dp[i-1][0] dp[i][0]=dp[i1][0]
for(int i=1; i<=n; i++){
    if(!reachable(i, 0)){
        dp[i][0] = 0;
    }else{
        dp[i][0] = dp[i-1][0];
    }
}
  1. 最上边一行 d p [ 0 ] [ j ] dp[0][j] dp[0][j]:与2.同理。
for(int j=1; j<=m; j++){
    if(!reachable(0, j)){
        dp[0][j] = 0;
    }else{
        dp[0][j] = dp[0][j-1];
    }
}
  1. 一般情况 d p [ i ] [ j ] dp[i][j] dp[i][j]:如果 m a p [ i ] [ j ] map[i][j] map[i][j] 是不可达位置, d p [ i ] [ j ] dp[i][j] dp[i][j]等于0;如果 m a p [ i ] [ j ] map[i][j] map[i][j] 可达,有两种方式可以到达:从 m a p [ i − 1 ] [ j ] map[i-1][j] map[i1][j] 向下走一步、从 m a p [ i ] [ j − 1 ] map[i][j-1] map[i][j1] 向右走一步,将这两种方式产生的路径条数加起来就是到达 m a p [ i ] [ j ] map[i][j] map[i][j] 位置的路径总数:
    d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][j] = dp[i-1][j] + dp[i][j-1] dp[i][j]=dp[i1][j]+dp[i][j1]

C++代码实现

在实现的时候需要注意: d p [ i ] [ j ] dp[i][j] dp[i][j] 中的值有可能会超过int的范围,应该用long

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

int n, m, x, y;
void input(){
    cin >> n >> m >> x >> y;
}

bool reachable(int x1, int y1){
    if((x1 == x && y1 == y)
    || ((abs(x1-x)+abs(y1-y)==3 && x1!=x && y1!=y))){
        return false;
    }else{
        return true;
    }
}

long** dp;
long solve(){
    dp = new long*[n+1];
    for(int i=0; i<=n; i++){
        dp[i] = new long[m+1];
    }

    // init: dp[i][0] = dp[0][j] = 1; dp[x1][y1] = 0;
    if(!reachable(0, 0)){
        // dp[0][0] = 0;
        return 0;
    }else{
        dp[0][0] = 1;
    }
    for(int i=1; i<=n; i++){
        if(!reachable(i, 0)){
            dp[i][0] = 0;
        }else{
            dp[i][0] = dp[i-1][0];
        }
    }
    for(int j=1; j<=m; j++){
        if(!reachable(0, j)){
            dp[0][j] = 0;
        }else{
            dp[0][j] = dp[0][j-1];
        }
    }

    // bottom-up calc
    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            if(!reachable(i, j)){
                dp[i][j] = 0;
            }else{
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
    }

    return dp[n][m];
}

int main() {
    
    input();
    cout << solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值