P1002 [NOIP2002 普及组] 过河卒
题目描述
棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示,A 点 (0,0)、B 点 (n,m),同样马的位置坐标是需要给出的。
![](https://img-blog.csdnimg.cn/img_convert/4c36d529ee15d409e0a7b622ef8a4f9c.png)
现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
输入格式
一行四个正整数,分别表示 B 点坐标和马的坐标。
输出格式
一个整数,表示所有的路径条数。
输入输出样例
输入 #1
6 6 3 3
输出 #1
6
说明/提示
对于 100% 的数据,1≤n,m≤20,0≤0≤ 马的坐标 ≤20。
【题目来源】
NOIP 2002 普及组第四题
思路
第一反应是用dfs写哈哈,只有60分时间超限了。
#include<stdio.h>
int count;
int n,m,x,y;//B点和马的坐标
int map[25][25];
int next[8][2]={{-2,-1},{-2,1},{-1,-2},{1,-2},{2,1},{2,-1},{-1,2},{1,2}};
int nextn[2][2]={{0,1},{1,0}};
void dfs(int a,int b)
{
if(a==n&&b==m)
{
count++;
return ;
}
for(int i=0;i<2;i++)
{
int ta=a+nextn[i][0],tb=b+nextn[i][1];
if(ta>=0&&ta<=n&&tb>=0&&tb<=m&&map[ta][tb]==0)
{
map[a][b]=1;
dfs(ta,tb);
map[a][b]=0;
}
}
}
int main()
{
scanf("%d %d %d %d",&n,&m,&x,&y);
map[x][y]=1;
for(int i=0;i<8;i++)
{
int nx=x+next[i][0],ny=y+next[i][1];
map[nx][ny]=1;
}
dfs(0,0);
printf("%d",count);
}
看到讨论说不能用dfs,就去老老实实学了动态规划。
https://zhuanlan.zhihu.com/p/365698607
dp题目最主要是找出状态转移方程。这道题我时参照了别人的样例模拟。得出
f(i,j)=f(i-1,j)+f(i)(j-1).因为初始位置时0,为了防止数组越界,把所有的坐标都加2.另外题目的结果可能很大,所以book数组要用long long。
代码
#include<stdio.h>
int n,m,x,y;
int next[8][2]={{1,2},{-1,2},{1,-2},{-1,-2},{2,1},{-2,1},{2,-1},{-2,-1}};
long long map[30][30],book[30][30];
int max(int x,int y)
{
return (x>y)?x:y;
}
int main()
{
scanf("%d %d %d %d",&n,&m,&x,&y);
n+=2;m+=2;x+=2;y+=2;
book[2][1]=1;//标记走过的点
map[x][y]=1;//标记马的位置
for(int i=0;i<8;i++)
{
int nx=x+next[i][0],ny=y+next[i][1];
map[nx][ny]=1;
}
for(int i=2;i<=n;i++)
for(int j=2;j<=m;j++)
{
if(map[i][j]==1)
continue;//判断是否在马附近
book[i][j]=book[i-1][j]+book[i][j-1];//状态转移方程
}
printf("%lld\n",book[n][m]);
}