4819: [Sdoi2017]新生舞会
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 783 Solved: 396
[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
思路{
比较裸的分数规划吧。。。
由于昨天HN女队出了一道最大权闭合子图+分数规划,辣鸡的我没有看出来.就决心练一练.
首先这是个二分图。虚拟源汇点连容量为1的边,考虑怎么得解。
分数规划的套路是∑ai/∑bi=g,移项得∑ai-g*∑bi=0然后二分答案g都可以了;
由于最大化g,考虑最大费用最大流,那么每两个连(-ai+g*bi)的边,直接费用流即可.
}
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <vector> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #define inf (1<<30) #define il inline #define RG register #define db double #define LL long long #define maxx 301 using namespace std; struct ed{int nxt,to,c;db w;}e[maxx*maxx*6]; int head[maxx*2],tot; void add(int u,int v,int c,db w){ e[tot].to=v;e[tot].nxt=head[u]; e[tot].c=c;e[tot].w=w;head[u]=tot++; } void ADD(int u,int v,int c,db w){ add(u,v,c,w); add(v,u,0,-w); } int pre[maxx],flow; db dis[maxx],ans; bool in[maxx]; queue<int>que; bool SPFA(int s,int t){ memset(dis,127,sizeof(dis));memset(in,false,sizeof(in));db SS=dis[0]; que.push(s),in[s]=true;dis[s]=0; while(!que.empty()){ int u=que.front();que.pop(),in[u]=false; for(int i=head[u];i!=-1;i=e[i].nxt){ if((!e[i].c)||dis[e[i].to]<=dis[u]+e[i].w)continue; int v=e[i].to;dis[v]=dis[u]+e[i].w; pre[v]=i; if(!in[v])in[v]=true,que.push(v); } }if(dis[t]==SS)return false; return true; } db get(int s,int t){ int u,p,sum=66666666;db Ans=0; for(u=t;u!=s;u=e[p^1].to)p=pre[u],sum=min(sum,e[p].c); for(u=t;u!=s;u=e[p^1].to)p=pre[u],e[p].c-=sum,e[p^1].c+=sum,Ans+=sum*e[p].w; flow+=sum;return Ans; } db mincostflow(int s,int t){flow=0;ans=0.000; while(SPFA(s,t))ans+=get(s,t);return ans; } int n,a[maxx][maxx],b[maxx][maxx]; #define eps 0.0000001 il void work(){ scanf("%d",&n); for(RG int i=1;i<=n;++i) for(RG int j=1;j<=n;++j)scanf("%d",&a[i][j]); for(RG int i=1;i<=n;++i) for(RG int j=1;j<=n;++j)scanf("%d",&b[i][j]); db l=0.000,r=1000000.000; while(r-l>eps){ memset(head,-1,sizeof(head));tot=0; db mid=(l+r)/2;for(int i=1;i<=n;++i)ADD(0,i,1,0); for(int i=n+1;i<=(n<<1);++i)ADD(i,((n<<1)|1),1,0); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)ADD(i,n+j,1,-a[i][j]+b[i][j]*mid); db aa=-mincostflow(0,((n<<1)|1)); if(aa>0)l=mid+eps;else r=mid-eps; }printf("%.6lf",(l+r)/2); } int main(){ work(); return 0; }