第十届山东省赛 - B - Flipping Game(DP)

题目链接: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;
}

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值