目录
题目概述:
AC代码:
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
#define maxsize 20
ll dp[maxsize+1][maxsize+1];
int d[9][2]={{0,0},{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}};
int main()
{
std::ios::sync_with_stdio(false);
int fx,fy,hx,hy;
cin>>fx>>fy>>hx>>hy;
dp[0][0]=1;
dp[0][1]=1,dp[1][0]=1;
for(int i=0;i<9;++i)
{
int nx=hx+d[i][0],ny=hy+d[i][1];
if(nx<0||ny<0||nx>fx||ny>fy)
continue;
dp[nx][ny]=-1;//-1标记控制点
}
for(int i=0;i<=fx;++i)
for(int j=0;j<=fy;++j)
{
if(dp[i][j]==-1)//如果已经是控制点
{
dp[i][j]=0;
continue;
}
if(dp[i][j]==1)
continue;
if(i)
dp[i][j]+=dp[i-1][j];
if(j)
dp[i][j]+=dp[i][j-1];
}
cout<<dp[fx][fy]<<endl;
return 0;
}
分析思路:
1.首先,我们定义dp[i][j]为到达坐标(i,j)的方法 那么除去边界的一些特殊情况 卒可以向右或者向下 那么dp[i][j]=dp[i-1][j]+dp[i][j],也就是前一步向右到达的方法数加上前一步向下到达的方法数。
2.接下来考虑一些特殊情况。首先应该知道对于一个方形棋盘,左边界上坐标到达只能通过向下到达,上边界上坐标只能通过向右到达,也就是对于j=0和i=0的情况。需要在循环中特殊处理。
3.接下来还剩下两件事情,一个是马的封锁,一个是递推初始条件。应该先处理哪个?应该是递推初始条件。如果顺序颠倒 第四个测试点(19,19,1,0)就会WA。为什么呢?在这里,我们用d[9][2]作为马封锁点的偏移量,把dp中对应位置改为-1,进入循环时检查,如果是-1就改成0直接退出,因为封锁过的点至始至终到达他的办法数量都只能是0。
4.那么递推初始条件呢?表面上好像很容易,dp[0][0]=1,dp[0][1]=1,dp[1][0]=1。但是问题就出在这里。有可能第一步向右或者向下路径就已经被封锁。如(19,19,1,0),从(0,0)出发只能向右走,dp[0][1]=0而不是1。所以要先初始化递推初始条件,再进行马的封锁,这样马的封锁能够覆盖前面初始条件。
5.最后一个坑点是,这题dp数组要使用long long类型,当靠近数据范围边界时,int类型会爆。