[SDOI2017]新生舞会

Posted Z-Y-Y-S

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2017]新生舞会相关的知识,希望对你有一定的参考价值。

Description

学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。有n个男生和n个女生参加舞会
买一个男生和一个女生一起跳舞,互为舞伴。Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 
a[i][j] ,表示第i个男生和第j个女生一起跳舞时他们的喜悦程度。Cathy还需要考虑两个人一起跳舞是否方便,
比如身高体重差别会不会太大,计算得出 b[i][j],表示第i个男生和第j个女生一起跳舞时的不协调程度。当然,
还需要考虑很多其他问题。Cathy想先用一个程序通过a[i][j]和b[i][j]求出一种方案,再手动对方案进行微调。C
athy找到你,希望你帮她写那个程序。一个方案中有n对舞伴,假设没对舞伴的喜悦程度分别是a‘1,a‘2,...,a‘n,
假设每对舞伴的不协调程度分别是b‘1,b‘2,...,b‘n。令
C=(a‘1+a‘2+...+a‘n)/(b‘1+b‘2+...+b‘n),Cathy希望C值最大。

Input

第一行一个整数n。
接下来n行,每行n个整数,第i行第j个数表示a[i][j]。
接下来n行,每行n个整数,第i行第j个数表示b[i][j]。
1<=n<=100,1<=a[i][j],b[i][j]<=10^4

Output

一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等

Sample Input

3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9

Sample Output

5.357143
二分答案mid
然后把每个匹配权值变成a[i][j]-mid*b[i][j](最优比例生成树的套路)
然后求出最大权值匹配
没有用KM算法,直接建网络流模型,权值取负,跑最小费用流
看最小费用是否小于0
此题卡常,不用结构体快1倍
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 #include<queue>
  7 using namespace std;
  8 int from[200001],next[200001],to[200001],cap[200001];
  9 double dis[200001];
 10 int num=1,head[501],path[501],n;
 11 double dist[501],a[101][101],b[101][101];
 12 bool vis[501];
 13 void add(int u,int v,int c,double d)
 14 {
 15   num++;
 16   next[num]=head[u];
 17   head[u]=num;
 18   to[num]=v;
 19   from[num]=u;
 20   cap[num]=c;
 21   dis[num]=d;
 22 }
 23 bool SPFA(int S,int T)
 24 {int i;
 25   queue<int>Q;
 26   memset(vis,0,sizeof(vis));
 27   memset(path,-1,sizeof(path));
 28   for (i=S;i<=T;i++)
 29     dist[i]=1e9;
 30   Q.push(S);
 31   dist[S]=0;
 32   while (Q.empty()==0)
 33     {
 34       int u=Q.front();
 35       Q.pop();
 36       vis[u]=0;
 37       for (i=head[u];i!=-1;i=next[i])
 38     if (cap[i])
 39       {
 40         int v=to[i];
 41         if (dist[v]>dist[u]+dis[i])
 42           {
 43         dist[v]=dist[u]+dis[i];
 44         path[v]=i;
 45         if (vis[v]==0)
 46           {
 47             vis[v]=1;
 48             Q.push(v);
 49           }
 50           }
 51       }
 52     }
 53   if (dist[T]==1e9) return 0;
 54   return 1;
 55 }
 56 double mincost(int S,int T)
 57 {int i;
 58   double ans=0;
 59   while (SPFA(S,T))
 60     {
 61       for (i=path[T];i!=-1;i=path[from[i]])
 62     {
 63       cap[i]-=1;
 64       cap[i^1]+=1;
 65     }
 66       ans+=dist[T];
 67     }
 68   return ans;
 69 }
 70 bool check(double mid)
 71 {int S,T,i,j;
 72   double ans;
 73   memset(head,-1,sizeof(head));
 74   num=1;S=0;T=2*n+1;
 75   for (i=1;i<=n;i++)
 76     {
 77       add(S,i,1,0);add(i,S,0,0);
 78       add(n+i,T,1,0);add(T,n+i,0,0);
 79     }
 80   for (i=1;i<=n;i++)
 81     {
 82       for (j=1;j<=n;j++)
 83     {
 84       double res=a[i][j]-mid*b[i][j];
 85       add(i,n+j,1,-res);add(n+j,i,0,res);
 86     }
 87     }
 88   ans=mincost(S,T);
 89   if (ans<0) return 1;
 90   return 0;
 91 }
 92 int main()
 93 {int i,j;
 94   cin>>n;
 95   for (i=1;i<=n;i++)
 96     {
 97       for (j=1;j<=n;j++)
 98     {
 99       scanf("%lf",&a[i][j]);
100     }
101     }
102   for (i=1;i<=n;i++)
103     {
104       for (j=1;j<=n;j++)
105     {
106       scanf("%lf",&b[i][j]);
107     }
108     }
109   double l=0,r=1000000;
110   while (r-l>1e-7)
111     {
112       double mid=(l+r)/2.0;
113       if (check(mid)) l=mid;
114       else r=mid;
115     }
116   printf("%.6lf\n",(l+r)/2.0);
117 }

 

以上是关于[SDOI2017]新生舞会的主要内容,如果未能解决你的问题,请参考以下文章

SDOI2017 新生舞会

[BZOJ4819][SDOI2017]新生舞会

[SDOI2017]新生舞会

[BZOJ]4819: [Sdoi2017]新生舞会

BZOJ4819: [Sdoi2017]新生舞会(01分数规划)

[SDOI2017] 新生舞会