题型:动态规划+组合数学
题意:两个长度为n的01串S1、S2,每次对m个数进行反转,求经过K次从S1变为S2的方法数。
分析:
显然是DP。
定义dp(i,j),表示经过i步后,当前S1和S2有j个位置不相同。
初始化dp(0,cnt) = 1,cnt为S1和S2不相同的位置数。
假设当前为dp(i,j),枚举m中有多少个相同,有多少个不同,根据组合数学的乘法法则进行状态转移。
状态转移方程为:
for x=0 to min(j,m) step 1
dp(i+1,j-x+m-x) = C(j,x) * C(n-j,m-x) * dp(i,j);
end
最终答案为dp(k,0)。
代码:
题意:两个长度为n的01串S1、S2,每次对m个数进行反转,求经过K次从S1变为S2的方法数。
分析:
显然是DP。
定义dp(i,j),表示经过i步后,当前S1和S2有j个位置不相同。
初始化dp(0,cnt) = 1,cnt为S1和S2不相同的位置数。
假设当前为dp(i,j),枚举m中有多少个相同,有多少个不同,根据组合数学的乘法法则进行状态转移。
状态转移方程为:
for x=0 to min(j,m) step 1
dp(i+1,j-x+m-x) = C(j,x) * C(n-j,m-x) * dp(i,j);
end
最终答案为dp(k,0)。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define MOD 1000000009
#define LL long long
using namespace std;
//预处理C(n,m)
int C[160][160];
void init(){
C[0][0] = 1;
for(int i=1;i<=150;i++){
C[i][0] = C[i][i] = 1;
for(int j=1;j<i;j++){
C[i][j] = (C[i-1][j]%MOD + C[i-1][j-1]%MOD)%MOD;
}
}
}
int dp[150][150];
int main(){
init();
int n,k,m;
char str1[123],str2[123];
while(~scanf("%d%d%d",&n,&k,&m)){
scanf("%s%s",str1,str2);
int cnt = 0;
for(int i=0;i<n;i++){
if(str1[i] != str2[i]){
cnt++;
}
}
memset(dp,0,sizeof(dp));
dp[0][cnt] = 1;
for(int i=0;i<k;i++){ //k次变化
for(int j=0;j<=n;j++){ //j位不同,n-j位相同
if(dp[i][j]){ //若dp(i,j)有值
for(int x=0;x<=j&&x<=m;x++){ //选x个不同的变化,选m-x个相同的变化
if(m-x <= n-j){
LL ts = (((LL)C[j][x]*(LL)C[n-j][m-x])%MOD*dp[i][j])%MOD;
dp[i+1][j-x+m-x] += ts;
dp[i+1][j-x+m-x] %= MOD;
}
}
}
}
}
printf("%d\n",dp[k][0]);
}
return 0;
}