B - Flipping Game(ZOJ 4114)

Time Limit : 1 Second      Memory Limit : 65536 KB

Source : 第十届山东省ACM省赛

Problem Link : ZOJ 4114

Author : Houge  Date : 2019-5-21

 

题目大意:

  给你一排n个灯泡,让你进行k轮操作,每轮操作可以点亮或熄灭m个不同的灯泡。然后给你这排灯泡的初态和末态,问你在k轮操作后能让这排灯泡到达末态的方案有多少种(对998244353取模)。

分析:

  DP,学习了点我跳转XD的代码和思路。这个题我们在比赛的时候也想过用dp,不过主攻dp的我太菜了写不出状态转移方程,汗颜啊。。。

  先来考虑状态转移方程:

  ·我们可以这样想:对于同一个灯泡来说,如果它初态和末态的状态一样,那么可以认为在这个过程中它被操作了偶数次,反之则认为它被操作了奇数次,可以用odd_num来记录奇数次操作的灯泡的个数。

  ·然后我们声明一个二维数组dp[i][j],代表经过i轮操作过后有j个灯泡被操作了奇数次的方案数。

  ·接着我们来想一下它的下一轮操作:从j个奇数次操作的灯泡中选出k个,使它们变成偶数次操作;然后偶数次操作的灯泡中会有m-k个灯泡变成奇数次操作,即下一个状态为:dp[i+1][j-k+m-k]。

  ·最后利用组合数便可得到状态转移方程:

    (注意要取模)

  然后再来考虑一下初值的问题:

  ·对于dp中所有元素的初值,可先赋值为0。

  ·对于初态和末态,第一种方法是将初态设为dp[0][0]=1,最后输出末态dp[k][odd_num],但这样会有一个问题,初态是明确的,但是末态时虽然奇数次操作灯泡的个数时对的,但是并不能确定是哪几个灯泡,会有错误(*经过测试应该会输出oddnum个数的所有方案和)。

  ·所以有了第二种方法,将初态设为dp[0][odd_num]=1,最后输出的是dp[k][0],这样初末态就都明确了。

   最后考虑几个细节问题:

  ·推荐用long long,因为最后答案比较大随时可能爆掉(未测试)。

  ·在计算时随时取模防止爆。

  ·在求组合数的时候也要取模(我在后来自己写的时候就是这个地方卡住了,最后还是看的题解才发现的)。

  到这儿这个问题就基本解决了~

代码:

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 const int MAXN=105,MOD=998244353;
 6 long long c[MAXN][MAXN],dp[MAXN][MAXN];
 7 
 8 int main()
 9 {
10     long long i,j,k;
11     for(i=0;i<MAXN;i++)    //求组合数
12         for(j=0;j<=i;j++) c[i][j]=1;
13     for(i=2;i<MAXN;i++)
14         for(j=1;j<i;j++)
15             c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;    //要取模!
16 
17     int t;
18     scanf("%d",&t);
19     while(t--)
20     {
21         memset(dp,0,sizeof(dp));    //初始化
22         long long n,r,m,oddnum=0;
23         char curr[MAXN],dist[MAXN];
24         scanf("%lld%lld%lld%s%s",&n,&r,&m,curr,dist);
25 
26         for(i=0;i<n;i++)
27             if(curr[i]!=dist[i]) oddnum++;
28 
29         dp[0][oddnum]=1;
30 
31         for(i=0;i<r;i++)    //dp
32             for(j=0;j<=n;j++)
33                 for(k=0;k<=m;k++)
34                 {
35                     if(j>=k&&n-j>=m-k)    
36                     {
37                         dp[i+1][j-k+m-k]=dp[i+1][j-k+m-k]+(dp[i][j]*c[j][k]%MOD*c[n-j][m-k]%MOD);
38                         dp[i+1][j-k+m-k]%=MOD;
39                     }
40                 }
41         printf("%lld\n",dp[r][0]);
42     }
43     return 0;
44 }

 

posted @ 2019-05-21 17:23  CSGO_BEST_GAME_EVER  阅读(738)  评论(0编辑  收藏  举报