新生舞会
Time Limit: 10 Sec Memory Limit: 128 MB
Description
学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。有n个男生和n个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴。
Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 a[i][j] ,表示第i个男生和第j个女生一起跳舞时他们的喜悦程度。
Cathy还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 b[i][j],表示第i个男生和第j个女生一起跳舞时的不协调程度。
当然,还需要考虑很多其他问题。Cathy想先用一个程序通过a[i][j]和b[i][j]求出一种方案,再手动对方案进行微调。
Cathy找到你,希望你帮她写那个程序。
一个方案中有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值最大。
第一行一个整数n。
接下来n行,每行n个整数,第i行第j个数表示a[i][j]。
接下来n行,每行n个整数,第i行第j个数表示b[i][j]。
Output
一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
Sample Output
5.357143
HINT
1<=n<=100,1<=a[i][j],b[i][j]<=10^4
Main idea
选择两个人<i,j>会获得A[i][j],以及B[i][j],选择后不能再选,要求使得ΣA[i][j]/ΣB[i][j]最大。
Solution
最大费用最大流 的话,可以把权值取相反数,然后跑最小费用最大流 。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 #include <bits/stdc++.h> using namespace std;typedef long long s64;const int ONE = 205 ;const int EDG = 25005 ;const double eps = 1e-6 ;const int INF = 21474836 ;int n,m;int A[ONE][ONE],B[ONE][ONE];int next[EDG],first[ONE],go[EDG],from[EDG],pas[EDG],tot;int vis[ONE],q[1000001 ],pre[ONE],tou,wei;double w[EDG],dist[ONE];int S,T;double Ans;inline int get () { int res=1 ,Q=1 ; char c; while ( (c=getchar ())<48 || c>57 ) if (c=='-' )Q=-1 ; if (Q) res=c-48 ; while ((c=getchar ())>=48 && c<=57 ) res=res*10 +c-48 ; return res*Q; } int Add (int u,int v,int flow,double z) { next[++tot]=first[u]; first[u]=tot; go[tot]=v; pas[tot]=flow; w[tot]=z; from[tot]=u; next[++tot]=first[v]; first[v]=tot; go[tot]=u; pas[tot]=0 ; w[tot]=-z; from[tot]=v; } bool Bfs () { for (int i=S;i<=T;i++) dist[i]=INF; tou = 0 ; wei = 1 ; q[1 ] = S; vis[S] = 1 ; dist[S] = 0 ; while (tou < wei) { int u = q[++tou]; for (int e=first[u];e;e=next[e]) { int v=go[e]; if (dist[v] > dist[u]+w[e] && pas[e]) { dist[v] = dist[u]+w[e]; pre[v] = e; if (!vis[v]) { q[++wei] = v; vis[v] = 1 ; } } } vis[u] = 0 ; } return dist[T] != INF; } double Deal () { int x = INF; for (int e=pre[T]; go[e]!=S; e=pre[from[e]]) x = min (x,pas[e]); for (int e=pre[T]; go[e]!=S; e=pre[from[e]]) { pas[e] -= x; pas[((e-1 )^1 )+1 ] += x; Ans += w[e]*x; } } int Check (double ans) { memset (first,0 ,sizeof (first)); tot=0 ; S=0 ; T=2 *n+1 ; for (int i=1 ;i<=n;i++) { Add (S,i,1 ,0 ); for (int j=1 ;j<=n;j++) Add (i,j+n, 1 ,-(A[i][j] - ans*B[i][j])); Add (i+n,T,1 ,0 ); } Ans = 0 ; while (Bfs ()) Deal (); return -Ans >= eps; } int main () { n=get (); for (int i=1 ;i<=n;i++) for (int j=1 ;j<=n;j++) A[i][j] = get (); for (int i=1 ;i<=n;i++) for (int j=1 ;j<=n;j++) B[i][j] = get (); double l = 0 , r = 1e4 ; while (l < r - 1e-7 ) { double mid = (l+r)/2.0 ; if (Check (mid)) l = mid; else r = mid; } if (Check (r)) printf ("%.6lf" , r); else printf ("%.6lf" , l); }