[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
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 }