题目大意:
有一个2×N(1≤N≤10000)的网格,从任意一格出发,每次走相邻的格子,可以横着,竖着,斜着走,问用最少的步数走完所有格子的方案数。
题解:
最少的步数,即为不重复走任何一个格子。
枚举第一步从哪一列出发,先走完左边或右边所有格子后,再走另一边,将两边的方案数相乘。如下图:
用动态规划来计算走完一边的方案数,要分两种情况:
1.走完一边不用回到出发的一列(但包含回到出发列的方案);
2.走完一边要回到出发的那一列。
dp[i][0]表示第一种情况的方案数;
dp[i][1]表示第二种情况的方案数。
也可以这样理解:
第一种情况表示:
第二种情况表示:
第一种情况转移:
- 只走一列:
- 走两列:
- 用下面第二种情况来走
第二种情况转移:
这时,每一列只能选一格来走,这样当往右走到底时,才能有空位回来
dp[i][0]=dp[i-1][0]*2+dp[i-2][0]*4+dp[i-1][1]*2;
dp[i][1]=dp[i-1][1]*2;
计算结果时,枚举起始的每一列,加上左边的方案数*右边的方案数*2。(这一列有两个格子可以选择)
ans+=dp[i-1][1]*dp[n-i[0]*2+dp[n-i][1]*dp[i-1][0]*2;
从第一列或最后一列出发需单独处理。
n=1时特判。
代码:
#include<cstdio>
#include<cstring>
const int MAXN=10005;
const long long MOD=1000000007LL;
int dp[MAXN][2];
int main()
{
int T,n;
long long ans;
dp[0][0]=1;
dp[0][1]=1;
dp[1][0]=2;
dp[1][1]=2;
for(n=2;n<=10000;n++)
{
dp[n][0]=((dp[n-1][0]*2LL)%MOD+(dp[n-1][1]*2LL)%MOD+(dp[n-2][0]*4LL)%MOD)%MOD;
dp[n][1]=(dp[n-1][1]*2LL)%MOD;
}
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
if(n==1)
printf("2\n");
else
{
ans=0;
ans=(ans+dp[n][0])%MOD;
for(int i=2;i<n;i++)
{
ans=(ans+(2LL*dp[i-1][1]*dp[n-i][0])%MOD)%MOD;
ans=(ans+(2LL*dp[n-i][1]*dp[i-1][0])%MOD)%MOD;
}
ans=(ans+dp[n][0])%MOD;
printf("%I64d\n",ans);
}
}
return 0;
}