关闭

ZOJ 3777 Problem Arrangement (状压DP)

标签: zoj数学dp状态压缩
214人阅读 评论(0) 收藏 举报
分类:

分析:题目要求数学期望,其实就是求最终值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;
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:56927次
    • 积分:2002
    • 等级:
    • 排名:第19912名
    • 原创:150篇
    • 转载:0篇
    • 译文:0篇
    • 评论:8条
    最新评论