P1002 [NOIP2002 普及组] 过河卒
题目描述
棋盘上 A A A 点有一个过河卒,需要走到目标 B B B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C C C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示, A A A 点 ( 0 , 0 ) (0, 0) (0,0)、 B B B 点 ( n , m ) (n, m) (n,m),同样马的位置坐标是需要给出的。
现在要求你计算出卒从 A A A 点能够到达 B B B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
输入格式
一行四个正整数,分别表示 B B B 点坐标和马的坐标。
输出格式
一个整数,表示所有的路径条数。
样例 #1
样例输入 #1
6 6 3 3
样例输出 #1
6
提示
对于 100 % 100 \% 100% 的数据, 1 ≤ n , m ≤ 20 1 \le n, m \le 20 1≤n,m≤20, 0 ≤ 0 \le 0≤ 马的坐标 ≤ 20 \le 20 ≤20。
思路
根据题意,卒只能向右或者向下走,题目中所要求的是到达B点的可行路径条数,我们知道,到达B点的可行路径条数等于紧挨着B点的左边的点对应的路径条数加上走到B点上面的点的可行路径条数,于是我们可以找到一个递推公式
dp[i][j] = dp[i-1][j] + dp[i][j-1];
既然得到了递推公式,我们首先考虑的方法便是动态规划,我们创造一个数组dp
dp[i][j]
代表走到(i,j)这个点的可行路径条数,
需要注意的是,题目中的马及马可以一步走到的地方是不可以让卒通过的,其对应的点的可行路径条数是0,为什么呢?我们可以考虑这个不可走的点的下一个点(遍历的下一个点),对于下一个点来说,卒没办法从这个不可走的点过来,所以对应的条数为0。
设马的坐标为(k.p),那么就可以得到
dp[k][p] = 0;
dp[k+1][p+2] = 0;
dp[k+2][p+1] = 0;
dp[k+2][p-1] = 0;
dp[k+1][p-2] = 0;
dp[k-1][p+2] = 0;
dp[k-2][p-1] = 0;
dp[k-2][p+1] = 0;
dp[k-1][p-2] = 0;
接下来初始化dp数组。先考虑边界的情况,x和y轴上的点对应的可行路径只有一条,因为x,y轴只对应着一条路径。所以对于x轴上的点可以得到
for(int i = 0; i <= n; i++){
dp[i][0] = 1;
}
同理对于y轴上的点有
for(int j = 0; j <= m; j++){
dp[0][j] = 1;
}
递推部分的代码也很容易得到,如果这个点可以走,那么进行递推,如果走不了,就直接跳过这个点
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(dp[i][j] != 0 ){
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
else{
continue;
}
}
}
由于我们对不可走的点赋值为0,所以初始化整个棋盘的时候不能全部初始化为0,我们可以初始化为-1
for(int i = 0; i < 50; i++){
for(int j = 0; j < 50; j++){
dp[i][j] = -1;
}
}
但问题来了,按照上述代码构造出的题解存在问题!
我们可以设想这种情况,马一步走到的位置如果在边界上,那么这个点的可行路径条数一定是0,但是这个点后面的点仍然被赋值为1
如果一个不可行点在边界上,那么其后面的点一定不可以被走到(因为卒只能向左或者向下走)所以我们就需要对边界赋值的代码进行修改
for(int i = 0; i <= n; i++){
if(dp[i][0] == 0){
for(int c = i; c <= n; c++){
dp[c][0] = 0;
}
}
else{
dp[i][0] = 1;
}
}
y轴同理
完整代码如下
#include<iostream>
using namespace std;
int main(){
int m,n,k,p;
cin>>n>>m>>k>>p;
for(int i = 0; i < 50; i++){
for(int j = 0; j < 50; j++){
dp[i][j] = -1;
}
}
dp[k][p] = 0;
dp[k+1][p+2] = 0;
dp[k+2][p+1] = 0;
dp[k+2][p-1] = 0;
dp[k+1][p-2] = 0;
dp[k-1][p+2] = 0;
dp[k-2][p-1] = 0;
dp[k-2][p+1] = 0;
dp[k-1][p-2] = 0;
for(int i = 0; i <= n; i++){
if(dp[i][0] == 0){
for(int c = i; c <= n; c++){
dp[c][0] = 0;
}
}
else{
dp[i][0] = 1;
}
}
for(int j = 0; j <= m; j++){
if(dp[0][j] == 0){
for(int k = j; k <= m; k++){
dp[0][k] = 0;
}
}
else{
dp[0][j] = 1;
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(dp[i][j] != 0 ){
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
else{
continue;
}
}
}
cout << dp[n][m];
return 0;
}
有错误请指出~