背景
快要期中考试了!老师需要hzy帮他排考试的座位。。。
描述
考场里的座位恰好有n行m列,并且恰好有n*m位考生在这个考场里面考试,也就是说,所有的座位上都有考生。hzy根据学校记载,有k位考生可能作弊,因此hzy不能让他们之中的任何两个人做在相邻的座位上!所谓相邻的座位,即在同一行相邻列或者在同一列的相邻行的座位。hzy准备这样安排座位,首先随机选择一种方案,如果这种方案是合法的,就用这种方案,否则重新选择。你的任务是计算,他得到一个合法方案时,需要的期望选择次数。
格式
输入格式
输入文件为一行,仅包含三个整数n,m和k。
输出格式
如果不存在合法的方案,则输出文件seating.out中应该包含Impossible!,否则输出一个分数p/q,表示期望选择次数(即平均次数),这里p和q应该是互质的。
样例1
样例输入1
1 4 3
样例输出1
Impossible!
样例2
样例输入2
2 3 2
样例输出2
15/8
提示
1≤n≤80,1≤m≤80,1≤n*m≤80
0≤k≤20,并且k≤n*m
分析:若选择f[i][j][k]表示到第i行第j列选k个有多少种方案, 那么f[i][j][k] 由f[i-1][j][k1] 和 f[i][j-1][k2]得来,但是座位两两之间不相连,行列之间的状态很难表示,所以选择用二进制表示状态。
设f[i][j][k] 表示 目前到第i行已选k个,第i行状态为j的方案数,
显然 j 要满足(j & (j>>1)) 为 0,i行自身的约束考虑好了,但是
第i行还要受到i-1行的约束,于是我们枚举第i-1行的状态,第i-1
行与第i行不冲突的条件为(t & (t>>1)) == 0和( t & j) == 0
(t为第i-1行的状态),然后我们可以得出转移方程:
f[i][j][k] += f[i-1][t][j-bit[k]](bit[k]为k的二进制中1的个数)
接下来分析k, k的最大值为2^z-1,z为n,m中较小的数,因为n*m<=80,所以z最大为8而已
因为题目要求最简分数, 所以要求ans与c[n*m][k]的最大公因数
第一次做状压dp的题QAQ,经验还是不足,要多做做啊!!
贴代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
ll dp[81][21][260], ans, c[81][81], ans1, d;
int bit[260];
ll gcd(ll a, ll b){
return !b?a:gcd(b,a%b);
}
int getbit(int x){
int ans = 0;
while(x){
x -= x&-x;
ans++;
}
return ans;
}
int main(){
int n, m, p, i, j, k, t;
scanf("%d%d%d", &n, &m, &p);
if(m > n) swap(n,m);
dp[0][0][0] = 1;
for(i = 0; i < (1<<m); i++) bit[i] = getbit(i);
for(i = 1; i <= n; i++){
for(j = 0; j <= p; j++){
for(k = 0; k < (1<<m); k++){
if(!(k&(k>>1)) && j >= bit[k]){//这一行的情况
for(t = 0; t < (1<<m); t++){//枚举上一行的情况
if(!(t&k) && !(t&(t>>1)) && bit[t]<=j-bit[k]){
dp[i][j][k] += dp[i-1][j-bit[k]][t];
}
}
}
}
}
}
for(j = 0; j < (1<<m); j++)
ans += dp[n][p][j];
if(!ans){
printf("Impossible!");
return 0;
}
for(i = 0; i <= n*m; i++){
c[i][0] = 1;
for(j = 1; j <= i; j++){
c[i][j] = c[i-1][j] + c[i-1][j-1];
}
}
ans1 = c[n*m][p];
d = gcd(ans, ans1);
ans /= d;
ans1 /= d;
printf("%lld/%lld", ans1, ans);
return 0;
}