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;
}

猜你喜欢

转载自www.cnblogs.com/wuyuanyuan/p/9087730.html