虽说是 dp 入门题,但还是有很多细节需要注意
如果设 \(f_{x,y}\) 为目标地点为 \((x,y)\) 时走的种数,那么答案就是 \(f_{n,m}\)
在不考虑那只讨厌的马的情况下,对于任意一个坐标 \((i,j)\) ,它能走的方案数应该是从上面和左边走来,即 \(f_{i-1,j}\ +\ f_{i,j-1}\) 的值(从上面走来的方案数和从左边走来的和)。
此时天空一声巨响,一匹马闪亮登场,从题目所给的图中不难发现,这只马影响九个位置的值,使过河卒不能走
换而言之,马能走的地方,过河卒的统计应该直接跳过。
那么一个显而易见的思路:设一个数组 \(s\) ,在开始 dp 前初始化 \(s\) 数组,将马可能走到的坐标打上标记,然后在 dp 的时候如果当前坐标有这个标记就 \(\mathtt{continue}\) ,否则令 \(f_{i,j}\ =\ f_{i-1,j}\ +\ f_{i,j-1}\),初始化 \(f_{1,1}\) 为 \(1\) 。
然而 NOI plus 的出题人会这么良心么???显然非常凉心。如果这样干,一开始就这么干,就会导致初始化 \(f\) 数组的值被干掉,最后结果都是 \(0\) 。所以需要判断一下二者的大小,取较大的值,从而避免这个问题,即 \(f_{i,j}\ =\ \max(f_{i,j},f_{i-1,j}+f_{i,j-1})\)
然而 NOI plus 的出题人的心仅仅这么凉么???显然比这要凉得多。由于转移方程设计减法,会导致数组访问一个不存在的负数下标!!!其实解决方法也很简单,也就是把棋盘往下移。
最后记得开 \(\mathtt{long\ long}\) !!!
参考代码
#include
#include
#include
#define ll long long
using namespace std;
ll f[101][101],n,m,mn,mm,s[230][230];
void stop(ll x,ll y)
{
s[x][y]=1;
s[x-1][y-2]=1;
s[x-2][y-1]=1;
s[x-2][y+1]=1;
s[x-1][y+2]=1;
s[x+1][y-2]=1;
s[x+2][y-1]=1;
s[x+1][y+2]=1;
s[x+2][y+1]=1;
}
//初始化 s 数组
int main()
{
scanf("%lld%lld%lld%lld",&n,&m,&mn,&mm);
n+=2,m+=2,mn+=2,mm+=2; //坐标下移
stop(mn,mm);
f[2][2]=1; //初始化下移
for(int i=2;i<=n;i++) //棋盘下移
{
for(int j=2;j<=m;j++) //棋盘下移
{
if(s[i][j]) continue;
f[i][j]=max(f[i][j],f[i-1][j]+f[i][j-1]);
}
}
printf("%lld\n",f[n][m]);
return 0;
}