HDU 5045 Contest 状态压缩DP

题目描述:

Description
In the ACM International Collegiate Programming Contest, each team consist of three students. And the teams are given 5 hours to solve between 8 and 12 programming problems.

On Mars, there is programming contest, too. Each team consist of N students. The teams are given M hours to solve M programming problems. Each team can use only one computer, but they can’t cooperate to solve a problem. At the beginning of the ith hour, they will get the ith programming problem. They must choose a student to solve this problem and others go out to have a rest. The chosen student will spend an hour time to program this problem. At the end of this hour, he must submit his program. This program is then run on test data and can’t modify any more.

Now, you have to help a team to find a strategy to maximize the expected number of correctly solved problems.

For each problem, each student has a certain probability that correct solve. If the i th student solve the j th problem, the probability of correct solve is P ij .

At any time, the different between any two students’ programming time is not more than 1 hour. For example, if there are 3 students and there are 5 problems. The strategy {1,2,3,1,2}, {1,3,2,2,3} or {2,1,3,3,1} are all legal. But {1,1,3,2,3},{3,1,3,1,2} and {1,2,3,1,1} are all illegal.

You should find a strategy to maximize the expected number of correctly solved problems, if you have know all probability

Input
The first line of the input is T (1 ≤ T ≤ 20), which stands for the number of test cases you need to solve.

The first line of each case contains two integers N ,M (1 ≤ N ≤ 10,1 ≤ M ≤ 1000),denoting the number of students and programming problem, respectively.

The next N lines, each lines contains M real numbers between 0 and 1 , the j th number in the i th line is P ij .

Output
For each test case, print a line “Case #t: ”(without quotes, t means the index of the test case) at the beginning. Then a single real number means the maximal expected number of correctly solved problems if this team follow the best strategy, to five digits after the decimal point. Look at the output for sample input for details.

Sample Input

1
2 3
0.6 0.3 0.4
0.3 0.7 0.9 

Sample Output

Case #1: 2.20000 

题目描述:

这道题真心难看懂….
简略地说就是n个人,m道题,每个人做每道题有一个值。求做所有题目的最大值。
但是!在任何时刻,无论哪个人都不能比其他所有人做的题多2道,或者更多。换句话说,如果n个人n道题,就必须一人一道。
这是一道网络赛的题。
我看大牛们博客上这道题有很多做法。但是最方便最被人认可的是其状态压缩做法,用dp[i][j]表示前i题,做过题的人的状态。用二进制表示。开始的时候我是看不懂这个表示的,因为一个人不是能做好多题么?
但是我发现由于限定条件,不可能有人做了两题或更多的时候有人没有做题,这说明我们在一个状态下只有做了一题,或者没做题。当所有人都做了一题的时候,把状态再置为0,(相当于所有人都没做题),重新开始更新状态,这样就能保证限定条件了。(真厉害!)也正是因为有这个限定条件,才能用状态压缩DP解答。
PS:这道题还可以用搭建网络流模型,用费用流做。每n个为一轮,用spfa跑m/n遍,也能保证限定条件。

下面是状压DP做法:

代码如下:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int MAXN =15;
const int MAXM =1010;

int T;
int n,m;
double a[MAXN][MAXM];
double dp[MAXM][1<<10];
int main()
{
    scanf("%d",&T);
    for(int t=1; t<=T; t++)
    {
        scanf("%d%d",&n,&m);
        memset(a,0,sizeof(a));
        for(int i=0; i<n; i++)
           for(int j=0; j<m; j++)
              scanf("%lf",&a[i][j]);
        for(int i=0; i<MAXM; i++)
        {
            for(int j=0; j<(1<<10); j++)
            {
                dp[i][j]=-1.0;
            }
        }
        dp[0][0]=0;
        for(int i=0; i<m; i++)//找第i道题
        {
            for(int j=0; j<(1<<n); j++)//找j状态
            {
                int s;//记录状态
                if (dp[i][j]<0) continue;
                for(int k=0; k<n; k++)//找第k个人
                {
                    if (!((1<<k) & j))//第k人没做过题
                    {
                        s=((1<<k) | j);//做题
                        if (s == (1<<n)-1) s=0;//所有人都做了一题清零
                        dp[i+1][s]=max(dp[i+1][s],dp[i][j]+a[k][i]);
                        //printf("%lf\n",dp[i+1][s]);
                    }
                }
            }
        }
        double sum=0.0;
        for(int i=0; i<=(1<<n)-1; i++)//寻找状态,哪个是最大状态
           sum=max(sum,dp[m][i]);
        printf("Case #%d: %.5lf\n",t,sum) ;
    }
    return 0;
}

你可能感兴趣的:(状压dp)