ZOJ - 4114 Flipping Game

ZOJ - 4114 Flipping Game 

  题目大意:给出两个串s,t,n个灯泡的序列,1代表开着,0代表关着,一共操作k轮,每轮改变m个灯泡的状态,问最终能把s串变成t串的方案数。

  坤神题解

  很奇特的一种dp,首先状态的定义dp[i][j]是前i轮操作后,有j个灯泡的状态跟最终串不一样的方案数。这样初始化是dp[0][num]=1其他全0,num是s串和t串状态不同的灯泡数目。然后状态的转移,对于每一轮我们枚举有j个灯泡跟最终串状态不一样,然后再枚举上一轮有kk个灯泡与最终串状态不一样,每次操作都会改变灯泡的状态,设上一轮不一样的状态改变了x个,一样的状态改变了y个,那么x,y应该有0<=x<=kk,0<=y<=n-kk,x+y=m,kk-x+y=j,这四个约束条件,而满足约束条件的就是合法的转移过程,也就是dp[i][j]=dp[i-1][kk]*C[kk][x]*C[n-kk][y]最后注意取模。

 1 #include<cstdio>
 2 typedef long long ll; 
 3 const int N=118,mod=998244353;
 4 ll dp[N][N],C[N][N];
 5 char a[N],b[N];
 6 void getC()
 7 {
 8     C[0][0]=1ll;
 9     for(int i=1;i<=100;i++)
10     {
11         C[i][0]=1ll;
12         for(int j=1;j<=i;j++)
13         {
14             if(j<=i/2)
15                 C[i][j]=C[i-1][j]+C[i-1][j-1];
16             else
17                 C[i][j]=C[i][i-j];
18             if(C[i][j]>=mod)
19                 C[i][j]%=mod;
20         }
21     }
22 }
23 void init(int n,int k)
24 {
25     for(int i=0;i<=k;i++)
26         for(int j=0;j<=n;j++)
27             dp[i][j]=0ll;
28     int num=0;
29     for(int i=0;i<n;i++)
30         num+=(a[i]!=b[i]);
31     dp[0][num]=1;
32 }
33 int main()
34 {
35     int t,n,m,k,x,y;
36     getC(); 
37     scanf("%d",&t);
38     while(t--)
39     {
40         scanf("%d%d%d",&n,&k,&m);
41         scanf("%s%s",a,b);
42         init(n,k);
43         for(int i=1;i<=k;i++)
44             for(int j=0;j<=n;j++)
45                 for(int kk=((m+j)&1);kk<=m+j&&kk<=n;kk+=2)
46                 {//把约束条件联立一下可以得出合法条件下的x,y 
47                     y=(m+j-kk)/2;
48                     x=m-y;
49                     if(x<0||y<0||x>kk||y>n-kk)
50                         continue;
51                     dp[i][j]+=dp[i-1][kk]*C[kk][x]%mod*C[n-kk][y]%mod;
52                     if(dp[i][j]>=mod)
53                         dp[i][j]%=mod;
54                 }
55         printf("%lld\n",dp[k][0]);
56     }
57     return 0;
58 }
神奇的dp
posted @ 2019-05-28 15:05  新之守护者  阅读(280)  评论(0编辑  收藏  举报