vijosP1286

解法:n*m<=80立即推,n和m至少存在一个小于等于8(证明:假设两个都超过8,即都大于等于9,9*9>80,矛盾)

用状压dp

每行的状态不会超过1<<8,

dp[i][j][r]第i行,总共j个人,状态为r的方案数

dp[i][j][r]=dp[i-1][j-bit[r]][t](bit[r]为r的二进制中1的个数,代表放了几个人,t为上一行的状态)

那么总的方案数就是dp[n][m][0]+dp[n][m][1]+...dp[n][m][(1<<m)-1]

假设期望步数为x,选中合法方案的概率为p=ans/c(n*m,k),x=p*1+(1-p)*(x+1) (成功了就只要1步,没成功就要多算一次)

x=1/p=c(n*m,k)/ans

#include<iostream>
typedef long long ll;
//dp[i][j][r]第i行,总共j个人,状态为r的方案数
ll dp[85][25][260] = { 1 };
ll gcd(ll a, ll b) {
	ll c = a % b;
	while (c) {
		a = b;
		b = c;
		c = a % b;
	}
	return b;
}
//获取2进制中1的个数
int getBit(int x) {
	int cnt = 0;
	while (x) {
		++cnt;
		//x&(-x)取到最低位的1
		x -= x & (-x);
	}
	return cnt;
}
int main() {
	ll ans = 0, numerator = 1, denominator = 1, temp;
	int n, m, k, bit[260] = {};
	scanf("%d%d%d", &n, &m, &k);
	//让m成为小的
	if (n < m) {
		n ^= m;
		m ^= n;
		n ^= m;
	}
	for (int i = 1 << m; i >= 0; --i)bit[i] = getBit(i);
	for (int i = 1; i <= n; ++i)//i行
		for (int j = 0; j <= k; ++j)//j人数
			for (int r = 0; r < (1 << m); ++r) {//r每行状态
				//r>>1右移,(r>>1)&r!=0说明有相邻的,j<bit[r]说明这一行放的人比目前总人数还多
				if ((r&(r >> 1)) || j < bit[r])continue;
				for (int t = 0; t < (1 << m); ++t) {//t上一行状态
					//t&r判断是否有上下相邻的1,bit[r]+bit[t]>j说明这两行放的人比总人数多
					if ((t&(t >> 1)) || (t & r) || bit[r] + bit[t] > j)continue;
					dp[i][j][r] += dp[i - 1][j - bit[r]][t];
				}
			}
	for (int i = 0; i < (1 << m); ++i)ans += dp[n][k][i];
	//x=p*1+(p-1)*(x+1) x=1/p=c(n*m,k)/ans
	if (ans) {
		//c(n*m,k)=(n*m)!/k!/(n*m-k)!=(n*m)*(n*m-1)*(n*m-2)...(n*m-k+1)/k!
		for (int i = 2; i <= k; ++i)denominator *= i;
		for (int i = n * m - k + 1; i <= n * m; ++i) {
			numerator *= i;
			temp = gcd(numerator, denominator);
			numerator /= temp;
			denominator /= temp;
		}
		denominator *= ans;
		temp = gcd(numerator, denominator);
		numerator /= temp;
		denominator /= temp;
		printf("%lld/%lld\n", numerator, denominator);
	}
	else printf("Impossible!\n");
	return 0;
}

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nightmare004

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值