P2182 翻硬币

分析:

60分的状压很好想:定义dp[ i ][ sta ]为翻了i次,状态为sta的方案数,枚举状态转移即可。

但n的范围是100。

遇到这种情况肯定不能状压了,一般是把记录状态换成记录其他东西。

比如说中国象棋这道题,就是将记录状态转换成记录个数。

这道题也是一样,记录初始状态与终止状态不同的个数。

枚举个数转移即可。

#include<bits/stdc++.h>
using namespace std;
#define N 105
#define ri register int
#define ll long long
const ll mod = 1e9+7;
int n,m,k,d=0;
ll dp[N][N],fac[N],invfac[N];
char a[N],b[N];
ll quick_pow(ll a,ll k)
{
    ll ans=1;
    while(k) { if(k&1) ans=ans*a %mod; a=a*a %mod; k>>=1; }
    return ans;
}
void init(int n)
{
    fac[0]=1;
    for(ri i=1;i<=n;++i) fac[i]=fac[i-1]*i %mod;
    invfac[n]=quick_pow(fac[n],mod-2);
    for(ri i=n;i>=1;--i) invfac[i-1]=invfac[i]*i %mod;
}
ll C(int n,int m)
{
    if(n<m) return 0;
    return fac[n]*invfac[n-m] %mod *invfac[m] %mod;
}
int main()
{
    scanf("%d%d%d",&n,&k,&m);
    init(n);
    scanf("%s%s",a+1,b+1);
    for(ri i=1;i<=n;++i) d+=(a[i]!=b[i]);
    dp[0][d]=1;
    for(ri i=1;i<=k;++i)
     for(ri j=0;j<=n;++j)//j个不同 
      for(ri r=0;r<=min(j,m);++r)//枚举的是翻了r个是j个不同里面的 
       if(j+m-2*r>=0 && j+m-2*r<=n)
        //翻了r个不同的,把它变成相同的,此时不同的还有:j-r个。
        //还剩 m-r个翻到了原来相同的,变成了m-r个不同的,所以新的不同的个数是:j+m-2*r 
        dp[i][j+m-2*r]=( dp[i][j+m-2*r] + dp[i-1][j]*C(j,r) %mod *C(n-j,m-r) %mod) %mod;
        //组合数:1.j个不同的选r个翻。2.n-j个相同的选m-r个翻 
    printf("%lld\n",dp[k][0]);
}
View Code

 

posted on 2019-11-06 21:35  rua-rua-rua  阅读(184)  评论(0编辑  收藏  举报