【BZOJ4819】[Sdoi2017]新生舞会 01分数规划+费用流
【BZOJ4819】[Sdoi2017]新生舞会
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
题解:正常的01分数规划步骤是,先二分答案r,然后将所有点的权值变成a[i]-r*b[i],取最大的k个数,如果最大的k个a[i]-r*b[i]的和>0,说明答案还可以更优,调整下界;否则调整上界
对于本题来说,我们不能直接排序取前n大,因此需要跑流量为n的最大费用流,如果总费用>0,则说明答案可以更优
(最大费用流不用说了吧?将边的费用取相反数,然后跑最小费用流)
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; int n,cnt,ans; double sum; int inq[210],head[210],next[100000],to[100000],flow[100000],re[210],rv[210]; double dis[210],cost[100000]; int A[110][110],B[110][110]; queue<int> q; void add(int a,int b,double c) { to[cnt]=b,cost[cnt]=-c,flow[cnt]=1,next[cnt]=head[a],head[a]=cnt++; to[cnt]=a,cost[cnt]=c,flow[cnt]=0,next[cnt]=head[b],head[b]=cnt++; } int bfs() { int i,u; for(i=1;i<=2*n+1;i++) dis[i]=9999999; dis[0]=0,q.push(0); while(!q.empty()) { u=q.front(),q.pop(),inq[u]=0; for(i=head[u];i!=-1;i=next[i]) { if(flow[i]&&dis[to[i]]>dis[u]+cost[i]) { dis[to[i]]=dis[u]+cost[i]; re[to[i]]=i,rv[to[i]]=u; if(!inq[to[i]]) inq[to[i]]=1,q.push(to[i]); } } } return dis[2*n+1]<9999999-1e-8; } bool solve(double sta) { memset(head,-1,sizeof(head)); int i,j; cnt=0,sum=0; for(i=1;i<=n;i++) { add(0,i,0),add(i+n,2*n+1,0); for(j=1;j<=n;j++) add(i,j+n,(double)A[i][j]-sta*B[i][j]); } while(bfs()) { sum+=dis[2*n+1]; for(i=2*n+1;i;i=rv[i]) flow[re[i]]--,flow[re[i]^1]++; } return sum<-1e-8; } int main() { scanf("%d",&n); int i,j,maxx; double l=0,r=0,mid; for(i=1;i<=n;i++) { maxx=0; for(j=1;j<=n;j++) { scanf("%d",&A[i][j]); maxx=max(maxx,A[i][j]); } r+=maxx; } for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&B[i][j]); while(r>=l+1e-8) { mid=(l+r)/2; if(solve(mid)) l=mid; else r=mid; } printf("%.6f",l); return 0; }
| 欢迎来原网站坐坐! >原文链接<