1. 程式人生 > 實用技巧 >P2182 翻硬幣 DP

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;
 }