Fork me on GitHub

2014 Super Training #4 B Problem Arrangement --状压DP

原题:ZOJ 3777  http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777

题意:给每个题目安排在每个位置的value。有一个程序随机选择安排的顺序,总的value值大于等于m时,就可以接受这个安排。问能够获得一次满足条件的安排的期望次数。

这题不会做,看网上是用状态压缩DP做的。

定义: dp[i][j]为选取了前i行,趣味和为j的方案种数。

由于程序是每次随机选择一个排列,每次的选择之间不影响,而且每次选中的概率相等,都为dp[S][m]/ fac[n] , 即满足条件的总数/ 总排列数。由于01分布的期望为p , 这里p = dp[S][m]/ fac[n],p为试验一次事件发生的概率,那么,事件一定发生试验的次数为1/p 。 所以结果为  fac[n]/dp[S][m] ,结果用最简形式表示。(S = (1<<n)-1). (参考玻璃年华Alex

方程:dp[i|(1<<j)][k+a[R][j]] += dp[i][k];   

 i 即一个01串,其中1的个数表示已经选择的行数,用R表示,则下一次要选第R+1行的数(由于下标从0开始,所以仍是a[R][j]),1的位置表示选取的前i行中选取的数的列的情况,下一次选的时候就不能选这些列了。 然后转移,如果k + a[R][j] > m,则令其为m即可,因为大于m后值就没有意义了。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 14

int fac[N],a[N][N];
int dp[5007][510];

void INIT_FAC()
{
    fac[1] = 1;
    for(int i=2;i<=12;i++)
        fac[i] = fac[i-1]*i;
}

int gcd(int a,int b)
{
    if(!b)
        return a;
    return gcd(b,a%b);
}

int main()
{
    int t,n,m,i,j,k,R;
    INIT_FAC();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                scanf("%d",&a[i][j]);
        memset(dp,0,sizeof(dp));
        dp[0][0] = 1;
        int S = (1<<n)-1;
        for(i=0;i<=S;i++)
        {
            R = 0;
            for(j=0;j<n;j++)
                if(i & (1<<j))
                    R++;
            //已访问R行
            for(j=0;j<n;j++) //放置第R+1行(下标仍为R),枚举其放置的列
            {
                if(i & (1<<j)) //这列已用过
                    continue;
                for(k=0;k<=m;k++)  //枚举和
                    dp[i|(1<<j)][min(k+a[R][j],m)] += dp[i][k];  //和超过m算作m
            }
        }
        if(dp[S][m])
        {
            int g = gcd(fac[n],dp[S][m]);
            printf("%d/%d\n",fac[n]/g,dp[S][m]/g);
        }
        else
            puts("No solution");
    }
    return 0;
}
View Code

 

posted @ 2014-07-01 21:35  whatbeg  阅读(247)  评论(0编辑  收藏  举报