【JZOJ】【DP】翻硬币

L i n k Link

J Z O J JZOJ 3921 3921

D e s c r i p t i o n Description

小X 离开家的时候忘记带走了钱包,掉下的硬币在桌子上排成了一列。正在等着哥哥回来的小Y坐在桌子旁边,无聊地翻着桌子上的硬币。
出于某种爱好,小Y 一次一定会同时翻转M 枚硬币。由于小Y 是一个爱动脑的小学生,这样进行了若干次之后她很快想到了一个问题:有多少种方法能够在K 次翻转后把硬币由原来的状态变成现在这样呢?
因为小Y 是个好学的小学生,她只需要你告诉她方案数对1000000007 取模的值以方便她进行验算就可以了。

I n p u t Input

第一行,包含三个字符N;K;M,表示硬币的数量,翻转的次数和每次翻转的硬币数量。
第2 3 行,包含N 个字母,表示硬币在一开始的状态和最终要变成的状态。1 表示正面而0 表示背面。

O u t p u t Output

一行包含一个整数,表示方案数对1000000007 取模的值。

S a m p l e Sample I n p u t Input

3 2 1
100
001

S a m p l e Sample O u t p u t Output

2

H i n t Hint

样例解释:
100->101->001
100->000->001
• 对于30% 的数据,N <=4; 0 <= K <= 5。
• 对于60% 的数据,N <= 10。
• 对于100% 的数据,1 <= N <= 100; 0 <= K <= 100; 0 <= M <= 	N。

T r a i n Train o f of T h o u g h t Thought

DP,动态转移方程:
f [ i ] [ f d ] = f [ i ] [ f d ] + f [ i 1 ] [ j ] C [ j ] [ k k ] C [ n j ] [ m k k ] f[i][fd] = f[i][fd] + f[i - 1][j] * C[j][kk] * C[n - j][m - kk]

C o d e Code

#include<iostream>
#include<cstdio>
const int Mod = 1000000007;

using namespace std;

int n, k, m, dif, fd;

long long C[101][101], f[101][101];
int a[5][101];
 

int main()
{
	C[0][0] = 1;
	scanf("%d%d%d", &n, &k, &m);
	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;
	}  
    for (int kk = 1; kk <= 2; ++kk)
	for (int i = 1; i <= n; ++i)
	 scanf("%1d", &a[kk][i]);
	for (int i = 1; i <= n; ++i) 
		if (a[1][i] != a[2][i]) ++dif;	//算出有多少个不同
	f[0][dif] = 1;
	for (int i = 1; i <= k; ++i)
	 for (int j = 0; j <= n; ++j)
	  for (int kk = 0; kk <= min(m, j); ++kk)//最多看能翻多少个
	  {
	  	if (n - j >= m - kk)
	  	{
	  		fd = j - kk + m - kk;//翻了之后有多少不同
	  		f[i][fd] = (f[i][fd] + f[i - 1][j] * (C[j][kk] * C[n - j][m - kk] % Mod) % Mod) % Mod;
	  	}
	  }
	printf("%lld", f[k][0]);
}

猜你喜欢

转载自blog.csdn.net/LTH060226/article/details/103643784