洛谷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;
}