P2182 翻硬币 DP

题意:

给定一个01序列,有K次操作,每次可以将M个元素翻转,给定初始初始状态和终止状态,求方案数

范围&性质:$1\le n\le 100,0\le k\le 100,0\le m\le n $

分析:

由于翻转不要求顺序,所以位置对方案没有任何影响,我们自然而然可以将点分为两类:与目标状态相同,与目标状态相反

然后我们可以设计DP状态,\(f[i][j]\)表示进行了\(i\)次操作有\(j\)个元素和目标状态不一样,推导出DP方程如下:

\[f[i][j-l+k-l]=\sum_{l=0}^k f[i-1][j]\times C_j^l\times C_{n-j}^{k-l} \]

表示第\(i\)次操作,减少了\(l\)个,增加了\(k-l\)个,最后的答案为\(f[k][0]\)

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	const long long mod= 1e9+7;
	long long n,k,m,cnt=0;
	long long f[105][105],c[105][105];
	char a[105],b[105];
	
	void init()
	{
		c[0][0]=1;
		c[1][0]=c[1][1]=1;
		for(int i=2;i<=100;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;
			}
		}
	}
	
	void work()
	{
		init();
		scanf("%lld%lld%lld",&n,&k,&m);
		scanf("%s",a+1);
		scanf("%s",b+1);
		for(long long i=1;i<=n;i++)
		{
			if(a[i]!=b[i])
			{
				cnt++;
			}
		}
		f[0][cnt]=1;
		for(long long i=1;i<=k;i++)
		{
			for(long long j=0;j<=n;j++)
			{
				for(long long l=0;l<=min(m,j);l++)
				{
					f[i][j-l+m-l]=(f[i][j-l+m-l]+(f[i-1][j]*c[j][l])%mod*c[n-j][m-l]%mod)%mod;
				}
			}
		}
		printf("%lld",f[k][0]);
	}
	
}

int main()
{
	zzc::work();
	return 0;
 } 
posted @ 2020-09-11 18:35  youth518  阅读(133)  评论(0编辑  收藏  举报