参考博客:2019山东省赛 ZOJ - 4114 Flipping Game dp
题目链接:传送门
【题意】:
有两个串,每次改变m个字符,改变k轮,把a变成b有多少种方法
改变 : 0 -> 1 , 1 -> 0
必须改变m个,然后k轮之后的结果是什么?
【题解】:
看了别人的博客才知道原来是一个状态转移。
dp[i][j],指的是 在第i轮改变后,有j个不同的位置。
题目其实只是关注两个串之间有多少个不同或相同展开讨论。
从dp[i-1][L] -> dp[i][j]
这个状态的转移:
就是有L个不同转到j个不同。
后来发现,转化需要组合数。
就是从已经不同的地方上,选择x个,然后从相同的地方选择y个。
满足条件为:
两个方程,两个未知数,可以求出x,y来
然后进行转移:
注意组合数中的x,y满足相应条件即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353 ;
const int N = 105;
ll dp[N][N];
ll C[N][N];
void Init(){
for(int i=0;i<N;i++){
C[i][0] = 1;
}
for(int i=1;i<N;i++){
for(int j=1;j<=i;j++){
C[i][j] = (C[i-1][j-1] + C[i-1][j])%mod;
}
}
}
char a[N],b[N];
int T,n,m,k,x,y,cnt;
int main()
{
Init();
scanf("%d",&T);
while(T--){
memset(dp,0,sizeof(dp));
cnt = 0;
scanf("%d%d%d",&n,&k,&m);
scanf("%s%s",a,b);
for(int i=0;i<n;i++){
cnt += (a[i] != b[i]);
}
dp[0][cnt] = 1;
for(int i=1;i<=k;i++){
for(int j=0;j<=n;j++){
for(int L=0;L<=n;L++){
if( j-L+m < 0 ) continue;
if( (j-L+m)&1 ) continue;
y = (j-L+m) / 2;
x = m - y;
if( x<0 || y<0 || x>L || y>n-L )continue;
dp[i][j] += dp[i-1][L] *C[L][x]%mod * C[n-L][y]%mod;
dp[i][j] %= mod;
}
}
}
printf("%lld\n",dp[k][0]);
}
return 0;
}