HDU 5045 Contest+条件改变

来源:互联网 发布:朱宸慧淘宝网店地址 编辑:程序博客网 时间:2024/06/01 22:23

Contest

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 269    Accepted Submission(s): 120


Problem 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 ith student solve the jth problem, the probability of correct solve is Pij .

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 jth number in the ith line is Pij .
 

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
12 30.6 0.3 0.40.3 0.7 0.9
 

Sample Output
Case #1: 2.20000
 

Source
2014 ACM/ICPC Asia Regional Shanghai Online



简单题,如果是因为做不出看这道题解的,建议重看一遍题目,看看有木有看错~~n只有10,马上就想到状态压缩,然后m只有1000,状态方程马上就出来了,dp[n][f]代表做到第N题时,哪些人目前领先的最优解,其中哪些人领先是由二进制表示,当f满人的时候,0这个状态就可以用来更新了。从前向后推比较简单。
代码:
#include<iostream>#include<cstdio>#include<cstdlib>#include<ctime>#include<string>#include<cstring>#include<algorithm>#include<fstream>#include<queue>#include<stack>#include<vector>#include<cmath>#include<iomanip>#define rep(i,n) for(i=1;i<=n;i++)#define MM(a,t) memset(a,t,sizeof(a))#define INF 1e9typedef long long ll;#define mod 1000000007using namespace std;double dp[1300][1300],val[1300][1300];bool vis[1300][1300];int n,m,nm;int main(){    int i,j,T,n2,k;        scanf("%d",&T);    rep(n2,T){         scanf("%d%d",&n,&m);         rep(i,n)            rep(j,m) scanf("%lf",&val[i][j]);      MM(dp,0); MM(vis,0); nm=(1<<n)-1;      dp[0][0]=0; vis[0][0]=true;       for(i=0;i<m;i++)        for(j=0;j<nm;j++)          for(k=1;k<=n;k++)            if(vis[i][j] && (j & (1<<(k-1)))==0){                 int ff=j|(1<<(k-1));                 vis[i+1][ff]=true;                 dp[i+1][ff]=max(dp[i+1][ff],dp[i][j]+val[k][i+1]);                 if(ff==nm){                  vis[i+1][0]=true;                dp[i+1][0]=max(dp[i+1][0],dp[i+1][ff]);                   }            }         double res=0;         for(i=0;i<nm;i++) res=max(res,dp[m][i]);         printf("Case #%d: %.5lf\n",n2,res);    }         return 0;}

然后此题也可以用费用流做,分段做10题10人做一组,一组一组搞下去就可以了,费用流具体细节就不多赘述了,

本文的重点并不在这,假如这题改成不是at anytime呢?即最终状态中任意两人做题时间不超过一小时,那样状态就太多了,用DP就不行了,即这题只能用费用流做了。

做法有很多,大体的模型是上下界费用流,但因为我对上下界费用流不是很了解,所以用了另一种方法,因为最终状态下,每个人是至少要做几道题的,设为ti,那会有m-ti*n道题还没有人做过,设总源点s0,另设两个点s1,s2。s0到s1的流是ti*n,然后s1分别流到题目的流为ti。s0到s2的流是m-ti*n,s2分别到题目的流是1。这样图就建好了,用最小费用最大流跑一遍即可。
参考代码(热烈欢迎来cha,毕竟没有题目可以验证对不对,其实我是为了上传一份费用流模板-W-)
#include<iostream>#include<cstdio>#include<cstdlib>#include<ctime>#include<string>#include<cstring>#include<algorithm>#include<fstream>#include<queue>#include<stack>#include<vector>#include<cmath>#include<iomanip>#define rep(i,n) for(i=1;i<=n;i++)#define MM(a,t) memset(a,t,sizeof(a))#define INF 1e9typedef long long ll;#define mod 1000000007using namespace std;int n,m,s0,s1,s2,t0,ti,nn;double val[1300][1300],cost[1300][1300];int flow[1300][1300];int edge[100000],nxt[100000],fir[1300],last[1300];int pre[1300];double dis[1300];bool vis[1300];queue<int>q;int spfa(){    int i;        for(i=0; i<=t0; i++)    {        vis[i]=false;        dis[i]=INF;    }    while(!q.empty()) q.pop();    vis[s0]=true;    dis[s0]=0;    q.push(s0);    while(!q.empty())    {        int k=q.front();        vis[k]=false;        q.pop();        for(i=fir[k];i!=-1; i=nxt[i])        {            int ee=edge[i];            if(flow[k][ee] && dis[ee]>dis[k]+cost[k][ee])            {                dis[ee]=dis[k]+cost[k][ee];                pre[ee]=k;                if(vis[ee]==false)                {                    vis[ee]=true;                    q.push(ee);                }            }        }    }    if(dis[t0]!=INF)        return 1;    else        return 0;}double fond(){    int i;    double j;    int Min=INF;    j=0;    while(spfa())    {        for(i=t0;i!=s0; i=pre[i])            Min=min(Min,flow[pre[i]][i]);        for(i=t0; i!=s0; i=pre[i])        {            flow[pre[i]][i]-=Min;            flow[i][pre[i]]+=Min;            j+=cost[pre[i]][i]*Min;        }    }    return j;}void add(int s,int e){    nn++; edge[nn]=e;    if(fir[s]==-1){      fir[s]=nn;      last[s]=nn;         }    else{      nxt[last[s]]=nn;      last[s]=nn;        }}int main(){    int i,j,T,n2,k;        scanf("%d",&T);    rep(n2,T){      scanf("%d%d",&n,&m);      MM(cost,0);      rep(i,n)        rep(j,m)          scanf("%lf",&val[i][j]);          MM(flow,0);       MM(last,-1); MM(nxt,-1); MM(fir,-1);      ti=m/n; s0=0; s1=n+m+1; s2=n+m+2; t0=s2+1; nn=0;      flow[0][s1]=ti*n; flow[0][s2]=m%n;      add(0,s1); add(s1,0); add(0,s2); add(s2,0);      rep(i,n){        flow[s1][i]=ti;        add(s1,i); add(i,s1);        flow[s2][i]=1;        add(s2,i); add(i,s2);      }      rep(i,n)        rep(j,m){          flow[i][n+j]=1;          add(i,n+j); add(n+j,i);          cost[i][n+j]=1-val[i][j];            cost[n+j][i]=val[i][j]-1;        }              rep(j,m){             flow[n+j][t0]=1;             add(n+j,t0); add(t0,n+j);         }          printf("Case #%d: %.5lf\n",n2,m-fond());    }         return 0;}



0 0
原创粉丝点击