【BZOJ4819】【SDOI2017】新生舞会 [费用流][分数规划]
新生舞会
Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss]
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值最大。
Input
第一行一个整数n。
接下来n行,每行n个整数,第i行第j个数表示a[i][j]。
接下来n行,每行n个整数,第i行第j个数表示b[i][j]。
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
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 #include<iostream>
2 #include<string>
3 #include<algorithm>
4 #include<cstdio>
5 #include<cstring>
6 #include<cstdlib>
7 #include<cmath>
8 using namespace std;
9 typedef long long s64;
10
11 const int ONE = 205;
12 const int EDG = 25005;
13 const double eps = 1e-6;
14 const int INF = 21474836;
15
16
17 int n,m;
18 int A[ONE][ONE],B[ONE][ONE];
19 int next[EDG],first[ONE],go[EDG],from[EDG],pas[EDG],tot;
20 int vis[ONE],q[1000001],pre[ONE],tou,wei;
21 double w[EDG],dist[ONE];
22 int S,T;
23 double Ans;
24
25 inline int get()
26 {
27 int res=1,Q=1; char c;
28 while( (c=getchar())<48 || c>57)
29 if(c=='-')Q=-1;
30 if(Q) res=c-48;
31 while((c=getchar())>=48 && c<=57)
32 res=res*10+c-48;
33 return res*Q;
34 }
35
36 int Add(int u,int v,int flow,double z)
37 {
38 next[++tot]=first[u]; first[u]=tot; go[tot]=v; pas[tot]=flow; w[tot]=z; from[tot]=u;
39 next[++tot]=first[v]; first[v]=tot; go[tot]=u; pas[tot]=0; w[tot]=-z; from[tot]=v;
40 }
41
42 bool Bfs()
43 {
44 for(int i=S;i<=T;i++) dist[i]=INF;
45 tou = 0; wei = 1;
46 q[1] = S; vis[S] = 1; dist[S] = 0;
47 while(tou < wei)
48 {
49 int u = q[++tou];
50 for(int e=first[u];e;e=next[e])
51 {
52 int v=go[e];
53 if(dist[v] > dist[u]+w[e] && pas[e])
54 {
55 dist[v] = dist[u]+w[e]; pre[v] = e;
56 if(!vis[v])
57 {
58 q[++wei] = v;
59 vis[v] = 1;
60 }
61 }
62 }
63 vis[u] = 0;
64 }
65 return dist[T] != INF;
66 }
67
68 double Deal()
69 {
70 int x = INF;
71 for(int e=pre[T]; go[e]!=S; e=pre[from[e]]) x = min(x,pas[e]);
72 for(int e=pre[T]; go[e]!=S; e=pre[from[e]])
73 {
74 pas[e] -= x;
75 pas[((e-1)^1)+1] += x;
76 Ans += w[e]*x;
77 }
78 }
79
80 int Check(double ans)
81 {
82 memset(first,0,sizeof(first)); tot=0;
83 S=0; T=2*n+1;
84 for(int i=1;i<=n;i++)
85 {
86 Add(S,i,1,0);
87 for(int j=1;j<=n;j++)
88 Add(i,j+n, 1,-(A[i][j] - ans*B[i][j]));
89 Add(i+n,T,1,0);
90 }
91
92 Ans = 0;
93 while(Bfs()) Deal();
94 return -Ans >= eps;
95 }
96
97 int main()
98 {
99 n=get();
100 for(int i=1;i<=n;i++)
101 for(int j=1;j<=n;j++)
102 A[i][j] = get();
103 for(int i=1;i<=n;i++)
104 for(int j=1;j<=n;j++)
105 B[i][j] = get();
106
107 double l = 0, r = 1e4;
108 while(l < r - 1e-7)
109 {
110 double mid = (l+r)/2.0;
111 if(Check(mid)) l = mid;
112 else r = mid;
113 }
114
115 if(Check(r)) printf("%.6lf", r);
116 else printf("%.6lf", l);
117 }