Bzoj4819 [Sdoi2017]新生舞会
Submit: 470 Solved: 242
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
HINT
Source
网络流 实数二分答案 费用流
C=(a'1+a'2+...+a'n)/(b'1+b'2+...+b'n)
为了让C值最大,可以二分找到最大的C使得 $(a'1+a'2+...+a'n)>C(b'1+b'2+...+b'n) $
二分费用后建边,跑最大费用最大流,检验最大费用是否为正数。
常数莫名大,自测只能过4个点。好在B站算总时,得以水过
1 /*by SilverN*/ 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 #include<queue> 8 using namespace std; 9 const int INF=1e9; 10 const double eps=1e-7; 11 const int mxn=100010; 12 int read(){ 13 int x=0,f=1;char ch=getchar(); 14 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 15 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 16 return x*f; 17 } 18 struct edge{ 19 int u,v,nxt,f; 20 double w; 21 }e[mxn<<1]; 22 int hd[205],mct=1; 23 inline void add_edge(int u,int v,int f,double w){ 24 e[++mct].v=v;e[mct].u=u;e[mct].nxt=hd[u];e[mct].f=f;e[mct].w=w;hd[u]=mct; 25 return; 26 } 27 inline void insert(int u,int v,int f,double w){ 28 add_edge(u,v,f,w); add_edge(v,u,0,-w); 29 return; 30 } 31 int n,m,S,T; 32 int a[105][105],b[105][105]; 33 /*void restore(double cost){ 34 for(register int i=2;i<=mct;i+=2){ 35 if(e[i^1].f){ 36 e[i].f+=e[i^1].f; 37 e[i^1].f=0; 38 } 39 if(e[i].v>n && e[i].v!=T){ 40 double res=a[e[i].u][e[i].v-n]-(double)b[e[i].u][e[i].v-n]*cost; 41 e[i].w=res; 42 e[i^1].w=-res; 43 } 44 } 45 return; 46 }*/ 47 double dis[205]; 48 bool inq[205]; 49 int pre[205]; 50 int q[mxn],hed,tl; 51 bool SPFA(){ 52 for(int i=S;i<=T;i++)dis[i]=-INF,pre[i]=0; 53 hed=tl=1; 54 q[hed]=S; 55 dis[S]=0; 56 while(hed<=tl){ 57 int u=q[hed++]; 58 inq[u]=0; 59 for(int i=hd[u];i;i=e[i].nxt){ 60 int v=e[i].v; 61 if(e[i].f && dis[v]<dis[u]+e[i].w){ 62 dis[v]=dis[u]+e[i].w; 63 pre[v]=i; 64 if(!inq[v]){inq[v]=1; 65 q[++tl]=v; 66 } 67 } 68 } 69 } 70 return dis[T]!=-INF; 71 } 72 double Res; 73 inline int min(int a,int b){return a<b?a:b;} 74 double MCF(){ 75 Res=0; 76 while(SPFA()){ 77 int tmp=INF; 78 for(int i=pre[T];i;i=pre[e[i].u])tmp=min(tmp,e[i].f); 79 for(int i=pre[T];i;i=pre[e[i].u]){ 80 e[i].f-=tmp; 81 e[i^1].f+=tmp; 82 Res+=e[i].w*tmp; 83 } 84 } 85 return Res; 86 } 87 void Rebuild(double lim){ 88 memset(hd,0,sizeof hd); 89 mct=1; 90 S=0;T=2*n+1; 91 for(int i=1;i<=n;i++){ 92 insert(S,i,1,0); 93 insert(i+n,T,1,0); 94 for(int j=1;j<=n;j++) 95 insert(i,j+n,1,a[i][j]-b[i][j]*lim); 96 } 97 return; 98 } 99 void solve(){ 100 double l=0,r=1e4,ans=0; 101 while(r-l>eps){ 102 double mid=(l+r)/2; 103 // restore(mid); 104 Rebuild(mid); 105 double res=MCF(); 106 if(res>0){l=mid;ans=mid;} 107 else r=mid; 108 } 109 printf("%.6f\n",ans); 110 } 111 int main(){ 112 int i,j; 113 n=read(); 114 for(i=1;i<=n;i++) 115 for(int j=1;j<=n;j++) 116 a[i][j]=read(); 117 for(i=1;i<=n;i++) 118 for(int j=1;j<=n;j++) 119 b[i][j]=read(); 120 solve(); 121 return 0; 122 }
本文为博主原创文章,转载请注明出处。