【BZOJ 4819】 4819: [Sdoi2017]新生舞会 (0-1分数规划、二分+KM)

4819: [Sdoi2017]新生舞会

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 601  Solved: 313

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

HINT

Source

 

【分析】

  好吧费用流竟然给过。。。

  但是我打KM。。。算练一练吧。。。还是打错了两个地方【要记住下次不要错了啊!!!

  就是经典的0-1分数规划

  二分答案mid

  则$\sum a[i][j]-mid*b[i][j]>=0$,这个用KM做最大费用判断是否大于等于0即可。

  然后精度要弄小一点才不会被卡,弄到了1e-10,然后狂T是因为INF不够大,晕。。。

 

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<algorithm>
 6 #include<cmath>
 7 using namespace std;
 8 #define Maxn 110
 9 const double eps=1e-10;
10 const double INF=1e12;
11 
12 double a[Maxn][Maxn],b[Maxn][Maxn];
13 
14 int visx[Maxn],visy[Maxn],match[Maxn];
15 double slack[Maxn],lx[Maxn],ly[Maxn];
16 int n;
17 
18 bool ffind(double mid,int x,int nt)
19 {
20     visx[x]=nt;
21     for(int y=1;y<=n;y++) if(visy[y]!=nt)
22     {
23         if(fabs(lx[x]+ly[y]-(a[x][y]-mid*b[x][y]))<eps)
24         {
25             visy[y]=nt;
26             if(!match[y]||ffind(mid,match[y],nt))
27             {
28                 match[y]=x;
29                 return 1;
30             }
31         }
32         else slack[y]=min(slack[y],lx[x]+ly[y]-(a[x][y]-mid*b[x][y]));
33     }
34     return 0;
35 }
36 
37 bool check(double mid)
38 {
39     for(int i=1;i<=n;i++) match[i]=0;
40     for(int i=1;i<=n;i++)
41     {
42         visx[i]=visy[i]=0;
43         lx[i]=-INF;ly[i]=0;
44         for(int j=1;j<=n;j++) lx[i]=max(lx[i],a[i][j]-mid*b[i][j]);
45     }int nt=0;
46     for(int i=1;i<=n;i++)
47     {
48         for(int j=1;j<=n;j++) slack[j]=INF;
49         while(1)
50         {
51             nt++;
52             if(ffind(mid,i,nt)) break;
53             double delta=INF;
54             for(int j=1;j<=n;j++) if(visy[j]!=nt) delta=min(delta,slack[j]);
55             for(int j=1;j<=n;j++)
56             {
57                 if(visx[j]==nt) lx[j]-=delta;
58                 if(visy[j]==nt) ly[j]+=delta;
59                 else if(fabs(INF-slack[j])<eps) slack[j]-=delta;
60             }
61         }
62     }
63     double ans=0;
64     for(int i=1;i<=n;i++) ans+=lx[i]+ly[i];
65     return ans>=0;
66 }
67 
68 int main()
69 {
70     double l=0,r=0;
71     scanf("%d",&n);
72     for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%lf",&a[i][j]),r+=a[i][j];
73     for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%lf",&b[i][j]);
74     while(r-l>eps)
75     {
76         double mid=(l+r)/2;
77         if(check(mid)) l=mid;
78         else r=mid;
79     }
80     printf("%.6lf\n",l);
81     return 0;
82 }
View Code

 

2017-04-28 13:21:57

posted @ 2017-04-28 13:21 konjak魔芋 阅读(...) 评论(...) 编辑 收藏