ZOJ-3777-Problem Arrangement(状态DP)

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

题意:

输入n和m,接下来一个n*n的矩阵,a[i][j]表示第i道题放在第j个顺序做可以加a[i][j]的分数,问做完n道题所得分数大于等于m的概率。用分数表示,分母为上述满足题意的方案数,分子是总的方案数,输出最简形式。

分析:

一开始用的DFS做的,复杂度太高,果断超时。。

正解是状压DP:

由于总的方案数为n! ,简化为求给一个n*n的矩阵,每一行每一列各选一个数使得n个数之和大于等于m的方案数。

n的范围是1 <= n <= 12,每一列选与不选分别用1和0表示,状态数最多达到1<<12。

dp[sta][score]表示状态为i得分为j的方案数。

sta通过二进制上1的位置来表示哪几列被选了。

在当前状态下,对于某一列j,若sta&(1<<j) == 0说明第j列还未选,继而可以由第j列来更新,否则说明第j列已经被选。

状态转移方程为:

  • dp[sta][g ] = sum(dp[i][g-a[ cnt+1][j]]);

    其中i当前状态的可行的上一状态,cnt为当前sta选到了第几行。

    由于从当前状态寻找上一状态很困难,所以我们反着来,用上一状态更新它可达的下一状态(具体看代码)

    最后dp[ (1<<n) -1 ][ m ]表示每行每列各取一个数,最后取n个数并得分大于等于m的方案数。(将动态转移方程变化了一下)

    说的不是很明白,代码写的很清楚。

    代码:

  • #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    
    int dp[1<<12][510];
    int f[13];
    int a[13][13];
    
    int gcd(int a, int b)
    {
        if(b == 0) return a;
        return gcd(b,a%b);
    }
    
    int main()
    {
        int test;
        int n,m;
    
        f[0] = 1;
        for(int i = 1; i <= 12; i++)
            f[i] = f[i-1] * i;
    
        scanf("%d",&test);
        while(test--)
        {
            scanf("%d %d",&n,&m);
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= n; j++)
                    scanf("%d",&a[i][j]);
    
            for(int i = 0; i < (1<<n); i++)
                for(int j = 0; j <= m; j++)
                    dp[i][j] = 0;
            dp[0][0] = 1;
    
            for(int i = 0; i < (1<<n); i++)
            {
                int cnt = 0;
                for(int j = 1; j <= n; j++)
                {
                    if(i & (1<<(j-1)) )
                        cnt++;
                }
    
                for(int j = 1; j <= n; j++)
                {
                    if(i & (1<<(j-1))) continue;
    
                    for(int g = 0; g <= m; g++)
                    {
                        if(g + a[cnt+1][j] >= m)
                            dp[i+(1<<(j-1))][m] += dp[i][g];
                        else
                            dp[i+(1<<(j-1))][g+a[cnt+1][j]] += dp[i][g];
                    }
                }
            }
            if(dp[(1<<n)-1][m] == 0)
                printf("No solution\n");
            else
            {
                int tmp = gcd(f[n],dp[(1<<n)-1][m]);
                printf("%d/%d\n",f[n]/tmp, dp[(1<<n)-1][m]/tmp);
            }
        }
    
        return 0;
    }
    时间: 2024-10-14 02:32:22

    ZOJ-3777-Problem Arrangement(状态DP)的相关文章

    ZOJ 3777 Problem Arrangement(状压DP)

    题目链接 : http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5264 //今年省赛的题目,比赛的时候知道是状压却一直没搞出,直到最后.虽然赛后知道做法,也一直没做的,最近想不开就来做了 - -, 顺便用了下快速枚举k-子集. 恩, 做法么就是开dp[i][j] i已经选过了的题目的一个集合,j表示的是获得了j分,然后就可以直接做了..(但是好像说会T或者卡空间,我的做法是快速枚举k-子集,这个东西可以看下watashi翻译的

    zoj 3777 Problem Arrangement

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5264 题意:给出n道题目以及每一道题目不同时间做的兴趣值,让你求出所有做题顺序中兴趣值大于等于m的比例.用一个分数表示. 状压dp. 枚举每一个状态,用二进制表示.dp[i][j]表示第i个题目,兴趣值为j的个数. 转移方程 dp[i|(1<<j)][k+a[num][j]]+=dp[i][k];兴趣值大于m的为 dp[1|(1<<j)][m]+=dp[i][k

    ZOJ-3777 Problem Arrangement(状态压缩DP)

    #include<stdio.h>#include<string.h>#include<algorithm>using namespace std;int dp[1<<13][510],map[13][13];int m,n;int gcd(long long a,long long b){ if(b!=0) return gcd(b,a%b); return a;}int main(){ int T; scanf("%d",&T

    B - Problem Arrangement ZOJ - 3777

    Problem Arrangement ZOJ - 3777 题目大意:有n道题,第i道题第j个做可以获得Pij的兴趣值,问至少得到m兴趣值的数学期望是多少,如果没有的话就输出No solution. 数学期望很好求,求出符合的方案数与总方案之比就是概率,概率的倒数就是期望.问题在于怎么安排这些题目的做题顺序,如果直接暴力的话,有12!种情况,铁定超时,所以我们可以转换成dp的思想,总共就是12道题,也就212-1种状态,我们枚举每个状态得多少分时的方案数,最终符合的方案数就是dp[212-1]

    2014 Super Training #4 B Problem Arrangement

    原题:ZOJ 3777  http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777 题意:给每个题目安排在每个位置的value.有一个程序随机选择安排的顺序,总的value值大于等于m时,就可以接受这个安排.问能够获得一次满足条件的安排的期望次数. 这题不会做,看网上是用状态压缩DP做的. 定义: dp[i][j]为选取了前i行,趣味和为j的方案种数. 由于程序是每次随机选择一个排列,每次的选择之间不影响,而且每次选中的概

    ZOJ 3551 Bloodsucker (概率DP)

    ZOJ Problem Set - 3551 Bloodsucker Time Limit: 2 Seconds      Memory Limit: 65536 KB In 0th day, there are n-1 people and 1 bloodsucker. Every day, two and only two of them meet. Nothing will happen if they are of the same species, that is, a people

    zoj 3791 An Easy Game dp

    An Easy Game Time Limit: 2 Seconds      Memory Limit: 65536 KB One day, Edward and Flandre play a game. Flandre will show two 01-strings s1 and s2, the lengths of two strings are n. Then, Edward must move exact k steps. In each step, Edward should ch

    ZOJ 3211 Dream City (J) DP

    Dream City Time Limit: 1 Second      Memory Limit: 32768 KB JAVAMAN is visiting Dream City and he sees a yard of gold coin trees. There are n trees in the yard. Let's call them tree 1, tree 2 ...and tree n. At the first day, each tree i has ai coins

    BZOJ 2298 problem a(DP)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2298 题意:一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低.”问最少有几个人没有说真话(可能有相同的分数) 思路:对于第i个人来说,区间[ai+1,n-bi]的人的分数相同.那么我们用sum[L][R]表示区间[L,R]中总人数.用f[i]表示前i个人中说真话的最大人数,那么f[j]=max(f[i-1]+sum[i][j]). map<pair<in

    ZOJ 3791 An easy game DP+组合数

    给定两个01序列,每次操作可以任意改变其中的m个数字 0变 1  1 变 0,正好要变化k次,问有多少种变法 dp模型为dp[i][j],表示进行到第i次变化,A,B序列有j个不同的 变法总和. 循环k次,每次针对m,向那j个不同 分1-j个即可,不过要用到组合数,因为对每个数操作不同都不一样 最后结果就是 dp[k][0] #include <iostream> #include <cstdio> #include <cstring> #include <alg