【洛谷U142297】下棋
题目:
题目链接:https://www.luogu.com.cn/problem/U142297
有 \(n\) 个白色棋子,\(m\) 个黑色棋子,现在需要把他们排成一排,要求对于任意一段棋子,其中的白色棋子和黑色棋子的差不能超过 \(t\),求棋子排列方案数对 \(10^9+7\) 取模的结果。
\(n,m\leq 150,t\leq 20\)。
思路
假设我们目前取了 \(i\) 个黑棋和 \(j\) 个白棋,在上一次取棋之前都是满足白色棋子和黑色棋子的差不超过 \(k\),那么如何判断此时黑白棋子的差是否超过 \(k\)?
我们可以分成黑减白以及白减黑两种方法计算,以黑减白为例,我们知道此时 \(i-j\) 的值,我们需要知道前面是否存在某一个时刻黑白棋子分别有 \(i',j'\) 个,且满足 \((i-i')-(j-j')>k\)。
显然上式等价于 \((i-j)+(j'-i')>k\),所以我们只需要知道之前白棋减黑棋的最大值即可判断。
所以设 \(f[i][j][k][l]\) 表示取了 \(i\) 个黑棋,\(j\) 个白棋,之前黑减白最大值为 \(k\),白减黑最大值为 \(l\) 的方案数。
转移就分下一个棋子取黑棋还是白棋即可。以拿黑棋来说,有
\[f[i+1][j][\max(k,i+1-j)][l]\gets f[i][j][k][l]
\]
时空复杂度均为 \(O(nmt^2)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=160,M=25,MOD=1e9+7;
int n,m,t,ans,f[N][N][M][M];
int main()
{
scanf("%d%d%d",&n,&m,&t);
f[0][0][0][0]=1;
for (int i=0;i<=n;i++)
for (int j=0;j<=m;j++)
for (int k=0;k<=t;k++)
for (int l=0;l<=t;l++)
{
if (i-j+l>t || j-i+k>t) f[i][j][k][l]=0;
if (i+1-j+l<=t && i<n)
f[i+1][j][max(i+1-j,k)][l]=(f[i+1][j][max(i+1-j,k)][l]+f[i][j][k][l])%MOD;
if (j+1-i-k<=t && j<m)
f[i][j+1][k][max(j+1-i,l)]=(f[i][j+1][k][max(j+1-i,l)]+f[i][j][k][l])%MOD;
}
for (int i=0;i<=t;i++)
for (int j=0;j<=t;j++)
ans=(ans+f[n][m][i][j])%MOD;
printf("%d",ans);
return 0;
}