ZOJ 3777 Problem Arrangement (状压DP)

分析:题目要求数学期望,其实就是求最终值M的概率。暴力的做法:找出所有排列方式,一个一个找:o(12!),贵了。对数据敏感的话,应该比较容易想到状压。
dp[i][msk][j]代表前i个数用了msk状态的数,和为j的排列总数。
那么转移方程:

dp[i][msk][j]=mskk1dp[i1][msk|1<<k][jp[k][i]]

其实第一维根本没什么意义,msk1的个数就代表了i的值,所有可以把第第一维去掉:p
dp[msk][j]=mskk1dp[msk|1<<k][jp[k][cnt[msk]]]

代码:(由于角标是从0开始的,所以和上面的有点不太一样)

#include <bits/stdc++.h>
#define LL long long
#define FOR(i,x,y)  for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)

using namespace std;

typedef vector <int> VT;

const int maxn = 1<<12;
const int maxm = 550;

LL dp[maxn][maxm];

int p[12][12],cnt[maxn][13];

int n,m;

LL gcd(LL a,LL b){
    return b == 0 ? a : gcd(b,a%b);
}

int main()
{
    //freopen("test.in","r",stdin);
    FOR(i,0,maxn){
        cnt[i][0] = 0;
        FOR(j,0,12){
            if(i & (1<<j)){
                cnt[i][++cnt[i][0]] = j;
            }
        }
    }
    int T;  scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        FOR(i,0,n)
            FOR(j,0,n)  scanf("%d",&p[i][j]);
        dp[0][0] = 1;
        int MSK = 1<<n;
        FOR(i,0,m){
            FOR(msk,1,MSK){
                dp[msk][i] = 0;
                FOR(j,1,cnt[msk][0]+1){
                    int k = cnt[msk][j];
                    if(i < p[k][cnt[msk][0]-1])  continue;
                    dp[msk][i] += dp[msk^(1<<k)][i-p[k][cnt[msk][0]-1]];
                }
            }
        }
        LL sum = 1;
        FOR(i,1,n+1){
            sum *= i;
        }
        LL res = 0;
        FOR(i,0,m){
            res += dp[MSK-1][i];
        }
        res = sum - res;
        if(!res){
            printf("No solution\n");
            continue;
        }
        LL g = gcd(res,sum);
        res /= g;
        sum /= g;
        printf("%lld/%lld\n",sum,res);
    }
    return 0;
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014610830/article/details/49934831
上一篇POJ 1222 EXTENDED LIGHTS OUT (异或消元)
下一篇HDU 1584 蜘蛛牌 (区间DP)
想对作者说点什么? 我来说一句

Problem Arrangement zoj 3777

2014年04月14日 1KB 下载

没有更多推荐了,返回首页

关闭
关闭