洛谷P1002题解--zhengjun

题目描述

棋盘上 \(A\) 点有一个过河卒,需要走到目标 \(B\) 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 \(C\) 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

棋盘用坐标表示,\(A\)\((0, 0)\)\(B\)\((n, m)\),同样马的位置坐标是需要给出的。

在这里插入图片描述

现在要求你计算出卒从\(A\) 点能够到达 \(B\) 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

输入格式

一行四个正整数,分别表示 \(B\) 点坐标和马的坐标。

输出格式

一个整数,表示所有的路径条数。

输入输出样例

输入 #1
6 6 3 3
输出 #1
6

说明/提示

对于\(100 \%\) 的数据,\(1 \le n, m \le 20,0 \le 马的坐标 \le20\)

思路

首先,可以打搜索,但这样,一个点就会被搜到好多次,复杂度太高,所以,最好用动态规划。
公式很简单,一个点,要么从它上面的点走来,要么从它左边的点走来,所以公式就是\(f[i][j]=f[i-1][j]+f[i][j-1]\)
这个很好理解。
最后,加\(long long\),虽然范围不大,但是,一次一次叠加,会爆\(int\)的。

不多说了,上代码

#include<bits/stdc++.h>
#define maxn 23
using namespace std;
int n1,m1,x2,y2;
long long dp[maxn][maxn];
int abs(int x){return x>0?x:-x;}
int main(){
	memset(dp,0,sizeof(dp));//初始值
    cin>>n1>>m1>>x2>>y2;
    n1++;m1++;x2++;y2++;//防止i-1或j-1出现数组越界
    dp[1][1]=1;//初始值
    for(int i=1;i<=n1;i++){
    	for(int j=1;j<=m1;j++){
    		if((i==x2&&j==y2)||((abs(i-x2)==1&&abs(j-y2)==2)||(abs(i-x2)==2&&abs(j-y2)==1)))//如果是能被马拦截的就还是为0
			    continue;
			if(i==1&&j==1)//如果是第一个点就不需要公式,没有这条语句的话,dp[1][1]也会变成0。
			    continue;
    		dp[i][j]=dp[i-1][j]+dp[i][j-1];
		}
	}
	cout<<dp[n1][m1];//递推结束
	return 0;
}

虽然这样已经可以\(AC\)了,但是,如果仔细想一想,在每一行中,只要上一行的就可以了,所以我们可以用一个二维度的数组,一行是当前行,另一行是上一行。
这样0,1的变化,可以用 ^ 来实现。(1 ^ 1=0,0 ^ 1=1)

代码

#include<bits/stdc++.h>
#define maxn 23
using namespace std;
int n1,m1,x2,y2;
long long dp[2][maxn];
int abs(int x){return x>0?x:-x;}
int main(){
	memset(dp,0,sizeof(dp));
    cin>>n1>>m1>>x2>>y2;
    n1++;m1++;x2++;y2++;
    dp[1][1]=1;
    for(int i=1;i<=n1;i++){
    	int ii=i%2;
    	for(int j=1;j<=m1;j++){
    		if((i==x2&&j==y2)||(abs(i-x2)==1&&abs(j-y2)==2)||(abs(i-x2)==2&&abs(j-y2)==1)){
    			dp[ii][j]=0;
    			continue;
			}
			if(i==1&&j==1)
			    continue;
    		dp[ii][j]=dp[ii^1][j]+dp[ii][j-1];
		}
	}
	cout<<dp[n1%2][m1];//注意不是dp[1][m1]
	return 0;
}

再仔细分析第一段代码,发现\(dp[i][j]\)是由\(dp[i-1][j],dp[i][j-1]\)两个子问题递推而来的。然而,能否保证再求\(dp[i][j]\)时,使\(dp[i-1][j],dp[i][j-1]\)能够知道。
其实,只要一维数组,让\(j\)从1到\(m1\),每次如果不会被马拦截,就直接加上\(dp[j-1]\),因为当前的未更新的\(dp[j]\)就是上一层的\(dp[j]\)
所以,可以再把二维度化为一维度。

代码

#include<bits/stdc++.h>
#define maxn 23
using namespace std;
int n1,m1,x2,y2;
long long dp[maxn];
int abs(int x){return x>0?x:-x;}
int main(){
	memset(dp,0,sizeof(dp));
    cin>>n1>>m1>>x2>>y2;
    n1++;m1++;x2++;y2++;
    dp[1]=1;
    for(int i=1;i<=n1;i++){
    	for(int j=1;j<=m1;j++){
    		if((i==x2&&j==y2)||(abs(i-x2)==1&&abs(j-y2)==2)||(abs(i-x2)==2&&abs(j-y2)==1)){
    			dp[j]=0;
    			continue;
			}
			if(i==1&&j==1)
			    continue;
    		dp[j]+=dp[j-1];
		}
	}
	cout<<dp[m1];
	return 0;
}

谢谢--zhengjun

posted @ 2022-06-10 18:21  A_zjzj  阅读(87)  评论(0编辑  收藏  举报