题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=6016
题目大意:有n个开关,有起始状态和终状态,问如果每次必须选m个开关进行改变状态,一共进行k次,那么有多少种方式可以从起始状态到终状态。
题目思路:首先可以发现,终状态与起始状态不同的开关一定被改变了奇数次,反之偶数次。设dp[i][j]表示第i轮有j个位置是被改变了奇数次。那么可以发现,假设这次有j个位置是奇数次,这次有k个位置从奇数次变成了偶数次,那么很明显,剩下m-k个位置就是从偶数次变成了奇数次。那么dp[i+1][j-k+m-k]+=dp[i][j]*c[j][k]*c[n-j][m-k]这个式子就出来了,c表示的是组合数,也就是下一轮的j-k+m-k(之前有j个奇数次位,减去了这次变成偶数位的个数k,再加上这次由偶数位变成奇数位的个数m-k),这也就是dp[i][j]中的j个奇数位中选择k个,从剩下的n-j个偶数位中选出m-k个,得到的就是如上的式子。
这个问题的第二个关键点在于初值的赋值。有两种显而易见的赋值方式,odd_num指的是两个串中状态不同的位置的个数,一种是开头dp[0][0],结束dp[k][odd_num],也就是刚开始没有奇数位变化的位,最后出现有奇数位变化,还有一种是dp[0][odd_num],dp[n][0],可以发现只有第二种是正确的,因为第一种到最后只能保证有odd_num个位发生变化,不能保证是哪几个,而如果第二种,就先保证了起始有m个位需要改变只有一种情况。
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int MAXN = 1e2+5;
const int MOD = 998244353;
ll dp[MAXN][MAXN];///dp[i][j]表示第i轮有j位是奇数的情况
ll c[MAXN][MAXN];///组合数
char s1[MAXN],s2[MAXN];
int main(){
rep(i,0,100)c[i][0]=1;
rep(i,1,100)c[i][i]=1;
rep(i,2,100){
rep(j,1,i-1){
c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
}
}
int t,n,k,m;
scanf("%d",&t);
while(t--){
scanf("%d%d%d%s%s",&n,&k,&m,s1,s2);
int num=0;
rep(i,0,n-1){
if(s1[i]!=s2[i])num++;
}
memset(dp,0,sizeof(dp));
dp[0][num]=1;
rep(i,0,k-1){
rep(j,0,n){///之前有j个奇数次变化的位置
rep(k,0,m){///有k个奇数变成了偶数
if(j>=k&&n-j>=m-k){
dp[i+1][j-k+m-k]+=dp[i][j]*c[j][k]%MOD*c[n-j][m-k]%MOD;
dp[i+1][j-k+m-k]%=MOD;
}
}
}
}
printf("%lld\n",dp[k][0]);
}
return 0;
}