【DP】翻硬币(jzoj 3921)

翻硬币

jzoj 3921

题目大意:

给你一个长度为 n n n的当前01串和目标01串,现在你要做 m m m此操作,每次操作你要使 k k k个不同的位取反,现在问你有多少种方法可以使当前01串变为目标01串

输入样例:

3 2 1
100
001

输出样例:

2

样例解释:

100 − > 101 − > 001 100->101->001 100>101>001
100 − > 000 − > 001 100->000->001 100>000>001

数据范围

对于30% 的数据, N < = 4 ; , 0 < = K < = 5 N <=4;,0 <= K <= 5 N<=4;,0<=K<=5
对于60% 的数据, N < = 10 N <= 10 N<=10
对于100% 的数据, 1 < = N < = 100 ; , 0 < = K < = 100 , 0 < = M < = N 1 <= N <= 100;,0 <= K <= 100,0 <= M <= N 1<=N<=100;,0<=K<=100,0<=M<=N

解题思路:

我们设 f i , j f_{i,j} fi,j为第 i i i次操作,与目标操作有 j j j位不同的种数
j ⩾ k j\geqslant k jk时( j < k j<k j<k的情况见代码)
我们从 f i − 1 , j f_{i-1,j} fi1,j转移到 f i , k f_{i,k} fi,k首先一定要取反 j − k j-k jk
多余的 m − ( j − k ) m-(j-k) m(jk)平分成与结果相同的和不同的来取反
这样我们就得出了状态转移方程:
f t , j = f t , j + f t − 1 , i ∗ C i m − ( j − k ) 2 ∗ C n − i j − i + ( m − ( j − k ) 2 )        ( j ⩾ k ) f_{t,j}=f_{t,j}+f_{t-1,i} * C_i^{\frac{m - (j - k)}{2}}* C_{n - i}^{j - i + (\frac{m - (j - k)}{2})}\ \ \ \ \ \ (j \geqslant k) ft,j=ft,j+ft1,iCi2m(jk)Cniji+(2m(jk))      (jk)
注:代码中的变量有所不同

代码:

5

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define abs(x) (x) < 0? -(x) : (x)
#define ll long long
#define wyc 1000000007//%%%
using namespace st
d;
ll n, m, k, s, C[150][150], f[150][150];
string str, str1;
int main()
{
	for (int i = 0; i <= 100; ++i)
		C[i][0] = 1;
	for (int i = 1; i <= 100; ++i)
		for (int j = 1; j <= 100; ++j)
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % wyc;//求组合数
	scanf("%lld%lld%lld", &n, &k, &m);
	cin>>str;
	cin>>str1;
	for (int i = 0; i < n; ++i)
		s += (str[i] != str1[i]);
	if (s&1 && !(m&1))//偶数无法拼成基数
	{
		printf("0");
		return 0;
	}
	f[0][s] = 1;
	for (ll t = 1; t <= k; ++t)
		for (ll i = 0; i <= n; ++i)
			for (ll j = max((i + m) & 1, i - m); j <= min(n, i + m); j += 2)//一些小优化
				if ((i + j + 1)&1 && abs(i - j) <= m)//也是一些小优化
				{
					if (j >= i)
						f[t][j] = (f[t][j] + f[t - 1][i] * C[i][((m - (j - i)) >> 1)] % wyc * C[n - i][j - i + ((m - (j - i)) >> 1)] % wyc) % wyc;//状态转移
					else f[t][j] = (f[t][j] + f[t - 1][i] * C[i][i - j + ((m - (i - j)) >> 1)] % wyc * C[n - i][((m - (i - j)) >> 1)] % wyc) % wyc;//反过来
				}
	printf("%lld", f[k][0]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值