[SDOI2017] 新生舞会
Posted ysfac
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2017] 新生舞会相关的知识,希望对你有一定的参考价值。
题意:
有n个男生和n个女生参加舞会,一个男生和一个女生互为舞伴。
已知第i个男生与第j个女生跳舞会产生$a_{i,j}$的喜悦度和$b_i,j$的不协调度。
你希望最终配对方案的$frac{sum{a_{i,j} }}{sum{b_{i,j} }}$最大,求这个最大值。
$nleq 100,1leq a_{i,j},b_{i,j}leq 10000$。
题解:
求一个最大化的比值基本就是分数规划了。
于是二分答案k,问题变为判断是否存在$sum{a_{i,j}-k imes b_{i,j}}geq 0$的方案。
显然直接跑个最大费用二分图匹配即可。
复杂度$O(msqrt{n} log{k})$。
套路:
- 求一个最大/最小化的比值基本就是分数规划了。
代码:
#include<bits/stdc++.h> #define maxn 505 #define maxm 1000005 #define inf 0x7fffffff #define eps 1e-10 #define ll long long #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; int n,nxt[maxm],hd[maxn],to[maxm],fl[maxm]; double A[maxn][maxn],B[maxn][maxn]; double cst[maxm],dis[maxn],Cost; int inq[maxn],vis[maxn],cnt,S,T; queue<int> q; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘; return x*f; } inline void addedge(int u,int v,int w,double c){ to[++cnt]=v,fl[cnt]=w,cst[cnt]=c,nxt[cnt]=hd[u],hd[u]=cnt; to[++cnt]=u,fl[cnt]=0,cst[cnt]=-c,nxt[cnt]=hd[v],hd[v]=cnt; } inline bool spfa(){ for(int i=1;i<=T;i++) dis[i]=2e9; q.push(S),inq[S]=1,dis[S]=0; while(!q.empty()){ int u=q.front(); q.pop(),inq[u]=0; for(int i=hd[u];i;i=nxt[i]){ int v=to[i]; if(fl[i]>0 && dis[v]>dis[u]+cst[i]){ dis[v]=dis[u]+cst[i]; if(!inq[v]) q.push(v),inq[v]=1; } } } return dis[T]<2e9; } inline int dfs(int u,int flow){ if(u==T) return flow; vis[u]=1; int sum=0; for(int i=hd[u];i;i=nxt[i]){ int v=to[i]; if(vis[v] || fl[i]<=0 || dis[v]!=dis[u]+cst[i]) continue; int f=dfs(v,min(flow,fl[i])); flow-=f,sum+=f,Cost+=f*cst[i],fl[i]-=f,fl[i^1]+=f; if(!flow) break; } if(!sum) dis[u]=2e9; return sum; } inline double Dinic(){ Cost=0; while(spfa()) memset(vis,0,sizeof(vis)),dfs(S,inf); return Cost; } inline bool check(double k){ memset(hd,0,sizeof(hd)),cnt=1; for(int i=1;i<=n;i++) addedge(S,i,1,0),addedge(i+n,T,1,0); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) addedge(i,j+n,1,k*B[i][j]-A[i][j]); return Dinic()<eps; } int main(){ n=read(),S=2*n+1,T=2*n+2; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) A[i][j]=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) B[i][j]=read(); double l=0,r=10000; while(l+eps<r){ double mid=(l+r)/2.0; check(mid)?l=mid:r=mid; } printf("%.6lf ",l); return 0; }
以上是关于[SDOI2017] 新生舞会的主要内容,如果未能解决你的问题,请参考以下文章