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 ",dp[k][0]); }