ZOJ 3777 - Problem Arrangement - [状压DP][第11届浙江省赛B题]

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3777

Time Limit: 2 Seconds      Memory Limit: 65536 KB

The 11th Zhejiang Provincial Collegiate Programming Contest is coming! As a problem setter, Edward is going to arrange the order of the problems. As we know, the arrangement will have a great effect on the result of the contest. For example, it will take more time to finish the first problem if the easiest problem hides in the middle of the problem list.

There are N problems in the contest. Certainly, it's not interesting if the problems are sorted in the order of increasing difficulty. Edward decides to arrange the problems in a different way. After a careful study, he found out that the i-th problem placed in the j-th position will add Pij points of "interesting value" to the contest.

Edward wrote a program which can generate a random permutation of the problems. If the total interesting value of a permutation is larger than or equal to M points, the permutation is acceptable. Edward wants to know the expected times of generation needed to obtain the first acceptable permutation.

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains two integers N (1 <= N <= 12) and M (1 <= M <= 500).

The next N lines, each line contains N integers. The j-th integer in the i-th line is Pij (0 <= Pij <= 100).

Output

For each test case, output the expected times in the form of irreducible fraction. An irreducible fraction is a fraction in which the numerator and denominator are positive integers and have no other common divisors than 1. If it is impossible to get an acceptable permutation, output "No solution" instead.

Sample Input

2
3 10
2 4 1
3 2 2
4 5 3
2 6
1 3
2 4

Sample Output

3/1
No solution

题意:

有n个题目,按从简单到难,若把第i难的题目放到第j个位置,会产生P[i][j]的“有趣度”;

现在有一个随机产生这n个题目排列的程序,若一个排列它的所有题目有趣度之和大于等于m,则算作满足要求;

求产生一个满足要求的题目排列的期望次数。

题解:

状压DP做法:state是一个n位的二进制数,每一位 1 or 0 代表了该位置是否被占掉了;

假设dp[state][k]代表:前i道题的放置情况按state安排,产生有趣度为k的方案数;

状态转移:

  若要计算cnt道题目按state安排情况下,dp[state][k]的值(所有有趣度k>m的方案都算在k=m里),则:

  从state里去掉一道题目,假设去掉的是放在第i个位置上的那道题目,得到new_state(这个new_state必然小于state),

  再枚举k=0~m,dp[state][(k+p[cnt][i])] += dp[new_state][k],同样记得把所有有趣度k>m的方案都算到k=m里。

正确性:

当state=0时,即初始dp[0][0]为1,dp[0][1~m]都为0,这是正确的,故可以从state=1开始状态转移;

同时,正如前面说的new_state必然小于state,我们从小到大枚举state,那么计算state时所有new_state都必然已经计算好了。

AC代码:

#include<bits/stdc++.h>
using namespace std;

int n,m;
int p[15][15];
int dp[1<<12][1300];

inline int gcd(int m,int n){return n?gcd(n,m%n):m;}

int fact[15];
void calcfact()
{
    fact[0]=1;
    for(int i=1;i<=12;i++) fact[i]=fact[i-1]*i;
}

int main()
{
    calcfact();

    int t;
    scanf("%d",&t);
    memset(p,0,sizeof(p));
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&p[i][j]);

        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int sta=1;sta<(1<<n);sta++) //遍历所有状态
        {
            int cnt=0; //cnt表示已经安排好了cnt道题目
            for(int i=1;i<=n;i++) if(sta&(1<<(i-1))) cnt++;

            for(int i=1;i<=n;i++)
            {
                if( ( sta & (1<<(i-1)) ) == 0 ) continue;

                for(int k=0;k<=m;k++)
                {
                    if(k+p[cnt][i]>=m) dp[sta][m]+=dp[sta^(1<<(i-1))][k];
                    else dp[sta][k+p[cnt][i]]+=dp[sta^(1<<(i-1))][k];
                }
            }
        }

        if(dp[(1<<n)-1][m]==0)
        {
            printf("No solution\n");
            continue;
        }

        int down=dp[(1<<n)-1][m];
        int up=fact[n];
        int g=gcd(up,down);
        printf("%d/%d\n",up/g,down/g);
    }
}

时间复杂度O(n2)+O(2nmn)+O(lg(n!)),显然在数据较大时,主要影响项是O(2nmn),根据数据规模(2^12)*12*500 ≈ 2e7,足够。

PS.自从上次做状压DP专题之后,很久没有再做状压DP的题目了,发现自己对状压DP的理解还是不够深刻,而且忘记地也很快,需要复习巩固。

猜你喜欢

转载自www.cnblogs.com/dilthey/p/8919445.html
今日推荐