P3705 [SDOI2017] 新生舞会
P3705 [SDOI2017] 新生舞会
题目描述
学校组织了一次新生舞会,Cathy 作为经验丰富的老学姐,负责为同学们安排舞伴。
有
Cathy 收集了这些同学之间的关系,比如两个人之前认识没,计算得出
Cathy 还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出
当然,还需要考虑很多其他问题。
Cathy 想先用一个程序通过
Cathy 找到你,希望你帮她写那个程序。
一个方案中有 n 对舞伴,假设每对舞伴的喜悦程度分别是
Cathy 希望
输出格式
一行一个数,表示
对于 100% 的数据,
Solution:
拿到题目的第一眼:完了我不会,再打开题目标签一看,
二分?费用流?我直接悟了
二分肯定是针对C来进行二分了,然后我们先推一下式子:
C=
然后我们再来看看x是怎么选的:
“有
也就是说,对于这整个网格图来说,每一行配对一列,然后检查当前的C是否可行。
那么如何求出
还记得之前我们看到过这题的第二个tag是费用流,所有我们不难想到利用
S向每个男生连一条流量为1,费用为0的边
每个男生向每个女生连一条流量为1费用为
每个女生向T连一条流量为1,费用为0的边
然后对于这个网络跑最大费用最大流,显然最大流一定能跑满n,这就保证了方案的可行性,然后在check一下费用是否大于等于0来作为check函数的返回值
然后就是注意一下由于本题是对一个小数进行二分,所以精度不能给得太高,我设的eps是1e-8,这样右边界转化为整数就是1e14左右,还是可以接受的,再有就是要注意dinic时是否忘记修改流量或者spfa时忘记修改队列状态而导致的TLE,RE等一系列问题半个小时的血泪教训
最短路只写dijkstra导致spfa丢三落四导致的
#include<bits/stdc++.h> const int N=505; const int ID=105; const int inf=1e9; const double eps=1e-8; using namespace std; int n,m,e_cnt=1,S,T; int head[N],a[N][N],b[N][N]; struct Edge{ int to,nxt,fl; double w; }e[N*N]; void add(int x,int y,int fl,double w) { e[++e_cnt]=(Edge){y,head[x],fl,w};head[x]=e_cnt; e[++e_cnt]=(Edge){x,head[y],0,-w};head[y]=e_cnt; //<<e_cnt<<"="<<x<<" "<<y<<" "<<fl<<" "<<w<<"\n"; } double dis[N]; int vis[N],flow[N],dl[N]; int pre[N]; void init() { for(int i=S;i<=T;i++) { dis[i]=-inf; flow[i]=vis[i]=pre[i]=0; } } queue<int> Q; bool spfa(int s,int t) { init(); dis[s]=0;flow[s]=inf; Q.push(s); while(!Q.empty()) { int u=Q.front();Q.pop(); dl[u]=0; if(++vis[u]>n)continue; //<<u<<" "<<dis[u]<<"\n"; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to,fl=e[i].fl; //<<v<<"="<<fl<<" ="<<dis[v]<<" "<<dis[u]+e[i].w<<"\n"; if(fl&&dis[v]<dis[u]+e[i].w) { dis[v]=dis[u]+e[i].w; flow[v]=min(flow[u],fl); pre[v]=i; if(!dl[v]) { dl[v]=1;Q.push(v); } } } //<<"\n"; } return flow[t]; } bool dinic() { double ans=0; while(spfa(S,T)) { int now=T,fl=flow[T]; ans+=1.0*fl*dis[T]; while(now!=S) { int id=pre[now]; e[id].fl-=fl;e[id^1].fl+=fl; //<<now<<" "; now=e[id^1].to; } //<<"="<<1.0*fl*dis[T]<<"\n"; } //<<ans<<"\n"; return ans>=0; } void build(double k) { for(int i=1;i<=n;i++) { add(S,i,1,0);add(i+ID,T,1,0); for(int j=1;j<=n;j++) { add(i,j+ID,1,1.0*a[i][j]-1.0*k*b[i][j]); } } } void INIT() { e_cnt=1; for(int i=S;i<=T;i++) { head[i]=0; } } bool check(double k) { INIT(); build(k); return dinic(); } void work() { cin>>n; S=0,T=ID<<1|1; for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){scanf("%d",&a[i][j]);}} for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){scanf("%d",&b[i][j]);}} double l=0,r=1000000,ans=0; while(l+eps<r) { double mid=(l+r)/2; if(check(mid)) { ans=mid; l=mid; } else { r=mid-eps; } //cout<<l<<r<<"" } printf("%lf",ans); } int main() { //freopen("dance.in","r",stdin); //freopen("dance.out","w",stdout); work(); return 0; }