解法: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;
}