Zoj 3777 Problem Arrangement Zoj 3777 Problem Arrangement

想法

求多少种排列的收益大等于 (m) 可以用状压dp。
(mask) 表示前 (i) 个数确定在哪几个位置。
(f(mask, j)) 表示前 (i) 个数位置确定,收益大等于 (j) 的方案数。

PS:比赛时想到折半,就上去写了,虽然发挥很稳定,但是还是写了好一会儿。还是状压好写,可惜没想出来。

代码

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define rep(i,a,b) for(int i=(a);i<(b);++i)
#define per(i,a,b) for(int i=(b)-1;i>=(a);--i)
#define sz(a) (int)a.size()
#define de(a) cout<<#a<<" = "<<a<<endl
#define dd(a) cout<<#a<<" = "<<a<<" "
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> vi;

const int N=1<<12, M=505;

int n,m;
int a[22][22], f[N][M], cdig[N], ini[22];

int calc(int x) {
	int res=0;
	while(x) {
		if(x&1) ++res;
		x>>=1;
	}
	return res;
}

int main() {
	rep(i,0,N) cdig[i]=calc(i);
	ini[0]=1;
	rep(i,1,22) ini[i]=ini[i-1]*i;
	int T;scanf("%d",&T);
	while(T--) {
		scanf("%d%d",&n,&m);
		rep(i,0,n) rep(j,0,n) scanf("%d",&a[i][j]);
		fill_n(f[0], M, 0);
		f[0][0]=1;
		rep(i,1,1<<n) {
			rep(j,0,m+1) {
				f[i][j]=0;
				rep(k,0,n) if(i>>k&1) {
					f[i][j]+=f[i^(1<<k)][max(0, j-a[cdig[i]-1][k])];
				}
			}
		}
		int fz=ini[n], fm=f[(1<<n)-1][m];
		if(fm==0) {
			puts("No solution");
			continue;
		}
		int d=__gcd(fz, fm);fz/=d;fm/=d;
		printf("%d/%d
",fz,fm);
	}
	return 0;
}