题目:
A点有一个过河卒,需要走到目标B点。卒行走规则:可以向下、或者向右。同时在棋盘上的任一点有一个对方的马(如上图的C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点。例如上图C点上的马可以控制9个点(图中的 P1,P2⋯P8和C)。卒不能通过对方马的控制点。
棋盘用坐标表示,A点(0,0)、B点(n,m)、C 点(cx,cy)(0<cx<n≤20,0<cy<m≤20)。现在要求你计算出过河卒从A 点能够到达B 点的路径的条数。注:象棋中马走“日”。
Input
输入4个整数,n,m,cx,cy,分别表示C 点的横坐标和C 点的纵坐标。
Output
输出一个整数,代表从A 点走到B 点的所有路径数。
Sample Input 1
5 5 2 4
Sample Output 1
14
思路:
理解了递推的核心思想其实这题并不难,就是先推倒他的关系式,都过关系式进行类似有dp的循环操作即可,本题中关系式不难得出,f(i,j) = f(i-1,j) + f(i,j-1)(因为只能向下或者向右移动),设置初始值为f(0,0) = 1,不过这题有一个陷阱就是dp数组要设置为long long 类型的,不然会超出范围
说到这里,我就想把我理解的递推的思想在总结一下,就以这道题为例,思想就是当你可以反过来想问题,倒着想,比如说向下走或者向右走,可以想像成当前的点是由他的上面的那个点或者是他的左边的那个点移动过来的,这样就不难推出递推的公式了,画一个图:
上代码:
#include"bits/stdc++.h"
using namespace std;
int n, m, cx, cy, sum = 0;
int vis[25][25];
long long dp[25][25]; // 一定要注意 这里有可能超范围
int main(){
cin >> n >> m >> cx >> cy;
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
if(cx-1 >= 0 && cy+2 <= m){
vis[cx-1][cy+2] = 1;
}
if(cx+1 <= n && cy+2 <= m){
vis[cx+1][cy+2] = 1;
}
if(cx+2 <= n && cy+1 <= m){
vis[cx+2][cy+1] = 1;
}
if(cx+2 <= n && cy-1 >= 0){
vis[cx+2][cy-1] = 1;
}
if(cx+1 <= m && cy-2 >= 0){
vis[cx+1][cy-2] = 1;
}
if(cx-1 >= 0 && cy-2 >= 0){
vis[cx-1][cy-2] = 1;
}
if(cx-2 >= 0 && cy-1 >= 0){
vis[cx-2][cy-1] = 1;
}
if(cx-2 >= 0 && cy+1 <= m){
vis[cx-2][cy+1] = 1;
}
vis[cx][cy] = 1;
dp[0][0] = 1;
for(int i = 0;i <= n;i ++){
for(int j = 0;j <= m;j ++){
if(i){
if(vis[i][j]) dp[i][j] = 0;
else dp[i][j] += dp[i-1][j]; // i对应i-1 !!! 要理解为啥
}
if(j){
if(vis[i][j]) dp[i][j] = 0;
else dp[i][j] += dp[i][j-1]; // j对应j-1 !!! if语句中代表向下走,改变的是列,所以这一步一定是要加上上面的那个位置对应的数值
}
}
}
cout << dp[n][m];
return 0;
}
爱父母,爱亲人,看淡人生,不忘初心