4819: [Sdoi2017]新生舞会(分数规划)
4819: [Sdoi2017]新生舞会
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1031 Solved: 530
[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]求出一种方案,再手动对方案进行微调。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
分析
首先是分数规划,然后用费用流判断。
$c=\frac{a_1+a_2+...+a_k}{b_1+b_2+...+b_k}$
二分c,如果c满足条件,那么$a_1+a_2+...+a_k \geq c*(b_1+b_2+...+b_k)$
在转化一下$(a_1-c*b_1)+(a_2-c*b_2)...+(a_k-c*b_k) \geq 0$
那么如果选i,j,他们的贡献就是a[i][j]-c*b[i][j],建图跑最大费用流即可。
可以把贡献取负,然后跑最小流。
注意要开double的变量
code
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 5 using namespace std; 6 7 const int N = 210; 8 const int INF = 1e9; 9 const double eps = 1e-8; 10 11 struct Edge{ 12 int from,to,nxt,cap;double cost; 13 }e[100100]; 14 int head[N],q[100100],pre[N]; 15 bool vis[N]; 16 int tot = 1,n,m,S,T; 17 int a[N][N],b[N][N]; 18 double dis[N]; 19 20 void add_edge(int u,int v,int cap,double cost) { 21 e[++tot].from = u;e[tot].to = v;e[tot].cap = cap;e[tot].cost = cost;e[tot].nxt = head[u];head[u] = tot; 22 e[++tot].from = v;e[tot].to = u;e[tot].cap = 0;e[tot].cost = -cost;e[tot].nxt = head[v],head[v] = tot; 23 } 24 void Clear() { 25 tot = 1; 26 memset(head,0,sizeof(head)); 27 } 28 bool spfa() { 29 for (int i=1; i<=T; ++i) 30 dis[i] = INF,vis[i] = false; 31 dis[S] = 0;vis[S] = true;pre[S] = 0; 32 int L = 1,R = 0; 33 q[++R] = S; 34 while (L <= R) { 35 int u = q[L++]; 36 for (int i=head[u]; i; i=e[i].nxt) { 37 int v = e[i].to; 38 if (e[i].cap && dis[v]-(dis[u]+e[i].cost)>=eps) { 39 dis[v] = dis[u] + e[i].cost; 40 pre[v] = i; 41 if (!vis[v]) q[++R] = v,vis[v] = true; 42 } 43 } 44 vis[u] = false; 45 } 46 if (dis[T] == INF) return false; 47 return true; 48 } 49 double MincostMaxflow() { // 返回double 50 double Mincost = 0; // double类型 51 while (spfa()) { 52 int minflow = INF; 53 for (int i=T; i!=S; i=e[pre[i]].from) 54 minflow = min(minflow,e[pre[i]].cap); 55 for (int i=T; i!=S; i=e[pre[i]].from) { 56 e[pre[i]].cap -= minflow; 57 e[pre[i] ^ 1].cap += minflow; 58 } 59 Mincost += minflow * dis[T]; 60 } 61 return Mincost; 62 } 63 bool check(double x) { 64 Clear(); 65 for (int i=1; i<=n; ++i) add_edge(S,i,1,0); 66 for (int i=1; i<=n; ++i) add_edge(i+n,T,1,0); 67 for (int i=1; i<=n; ++i) 68 for (int j=1; j<=n; ++j) 69 add_edge(i,j+n,1,-(a[i][j]-1.0*x*b[i][j])); 70 double ans = MincostMaxflow(); 71 return ans <= 0; 72 } 73 int main () { 74 scanf("%d",&n); 75 for (int i=1; i<=n; ++i) 76 for (int j=1; j<=n; ++j) 77 scanf("%d",&a[i][j]); 78 for (int i=1; i<=n; ++i) 79 for (int j=1; j<=n; ++j) 80 scanf("%d",&b[i][j]); 81 S = n*2+1;T = n*2+2; 82 double L = 0.0,R = 10000.0,ans; 83 while (R-L >= eps) { 84 double mid = (L + R) / 2; 85 if (check(mid)) ans = mid,L = mid; 86 else R = mid; 87 } 88 printf("%.6lf",ans); 89 return 0; 90 }