当前位置: 代码迷 >> 综合 >> ZOJ - 4114 Flipping Game(dp)
  详细解决方案

ZOJ - 4114 Flipping Game(dp)

热度:0   发布时间:2024-02-19 19:57:48.0

题目链接:点击查看

题目大意:给出一个长度为 n 的 01 字符串表示 n 个灯泡的状态,1 为点亮,0 为熄灭,现在需要进行 k 轮操作,每轮操作可以选择恰好 m 个位置,将灯泡的状态置反,现在给出初始状态和终止状态,问有多少种方案可以到达

题目分析:首先不难看出,如果对一个相同的位置操作偶数次,不会改变其状态,只有操作次数为奇数时才会改变其状态,那么设计二维 dp ,dp[ i ][ j ] 代表的是到达第 i 轮操作时,共有 j 次操作次数为奇数的方案数,转移的话,就是枚举 k ,代表的是当前选择 k 个位置从偶数变为奇数,因为一共需要操作 m 次,所以对应的需要将 m - k 个位置从奇数变为偶数,因为初始时有 j 次操作为奇数,n - j 次操作为偶数,所以满足上述 k 的方案数为 C[ j ][ k ] * C[ n - j ][ m - k ] ,简单的组合数学,然后转移就好了

有点没搞清楚的就是初始话,设初始状态和终止状态共有 num 个位置不同,则需要给 dp[ 0 ][ num ] 初始为 1 ,答案是 dp[ k ][ 0 ] ,而不能是 dp[ 0 ][ 0 ] = 0 ,然后输出 dp[ k ][ num ] 

代码:
 

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=110;const int mod=998244353;LL C[N][N],dp[N][N];char s1[N],s2[N];void init()//预处理组合数
{C[0][0]=1;for(int i=1;i<N;i++){C[i][0]=1;for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;}
}int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);init();int w;cin>>w;while(w--){memset(dp,0,sizeof(dp));int n,m,k;scanf("%d%d%d",&n,&k,&m);scanf("%s%s",s1,s2);int num=0;for(int i=0;i<n;i++)num+=(s1[i]!=s2[i]);dp[0][num]=1;for(int i=0;i<k;i++)//迭代k次 for(int j=0;j<=n;j++)//有j个位置操作了奇数次 for(int k=0;k<=m;k++)//有k个位置由奇数次变为了偶数次 if(j>=k&&n-j>=m-k)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;printf("%lld\n",dp[k][0]);}return 0;
}

 

  相关解决方案