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\n",fz,fm);
}
return 0;
}