[SDOI2017]新生舞会
Posted hbxblog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SDOI2017]新生舞会相关的知识,希望对你有一定的参考价值。
题目链接
(Describe)
有一场舞会,n个男生,n个女生,要组成n对舞伴,男生i和女生j组队的适合度是(a_{ij}),
不适合度是(b_{ij}),
让你求(max(sum(适合度)/sum(不适合度)))
(Solution)
这道题是(01)分数规划的好题目。我们首先拆分这个式子:
令(A_i)为舞伴的适合度,(B_i)为不适合度
[C=sum_{i=1}^{i<=n}a_i/sum_{i=1}^{i<=n}b_i]
[sum_{i=1}^{i<=n}a_i=sum_{i=1}^{i<=n}b_i*C]
[sum_{i=1}^{i<=n}a_i-sum_{i=1}^{i<=n}b_i*C=0]
则我们可以对C二分一个数(mid)
[令:ans=max(sum_{i=1}^{i<=n}a_i-sum_{i=1}^{i<=n}b_i*mid)]
如果(ans>0)则(ans)比(C)要小,反之比(C)大
那么这个(max(ans)怎么求呢?)
这个随便用个费用流搞一搞就好了;
- 把每个人拆成(x)和(x')
- 将(S)和(x)相连,流量为1,费用为0
- 将(x')和(T)相连,流量为1,费用为0
- 对于两个人(x,y)我们将(x)和(y')相连,流量为1,费用为(a[x][y]-mid*b[x][y])(因为我的spfa是跑的最短路,所以我写的是-a[x][y]+mid*b[x][y],最后跑出来的答案的绝对值就是最大费用最大流);
- 跑一遍最大费用最大流
(Code)
#include<bits/stdc++.h>
using namespace std;
const double inf=1e9+7;
typedef long long ll;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9')
x=x*10+c-'0',c=getchar();
return x*f;
}
struct node{
int to,next,v;
double w;
}a[1000001];
int f[10001],pre[10001],fa[10001],s,t=1000,head[10001],cnt;
double dis[10001];
void add(int x,int y,int c,double v){
a[++cnt].to=y,a[cnt].next=head[x],a[cnt].v=c,a[cnt].w=v,head[x]=cnt;
a[++cnt].to=x,a[cnt].next=head[y],a[cnt].v=0,a[cnt].w=-v,head[y]=cnt;
}
queue<int>q;
int spfa(){
q.push(s);
for(int i=s;i<=t;i++)
dis[i]=inf;
memset(f,0,sizeof(f));
f[s]=1,dis[s]=0;
while(!q.empty()){
int now=q.front();
q.pop();
f[now]=0;
for(int i=head[now];i;i=a[i].next){
int v=a[i].to;
if(dis[v]>dis[now]+a[i].w&&a[i].v){
dis[v]=dis[now]+a[i].w,pre[v]=i,fa[v]=now;
if(!f[v])
f[v]=1,q.push(v);
}
}
}
if(dis[t]!=inf)
return 1;
return 0;
}
double ans1,ans;
void anser(){
ans=0,ans1=0;
while(spfa()){
int minx=2147483647;
for(int i=t;i!=s;i=fa[i])
minx=min(minx,a[pre[i]].v);
ans+=minx,ans1+=dis[t]*minx;
for(int i=t;i!=s;i=fa[i])
a[pre[i]].v-=minx,(pre[i]%2)?a[pre[i]+1].v+=minx:a[pre[i]-1].v+=minx;
}
}
double A[1001][1001],B[1001][1001];
int n;
bool check(double x){
memset(head,0,sizeof(head)),cnt=0;
for(int i=1;i<=n;i++)
add(s,i,1,0),add(i+n,t,1,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
add(i,j+n,1,-A[i][j]+x*B[i][j]);
}
anser();
return (-ans1)>=0;
}
int main(){
n=read();
double l=0,r=10000,maxx=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>A[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>B[i][j];
while(r-l>=1e-7){
double mid=(l+r)*0.5;
if(check(mid))
l=mid,maxx=max(maxx,mid);
else r=mid;
}
printf("%0.6lf",maxx);
}
以上是关于[SDOI2017]新生舞会的主要内容,如果未能解决你的问题,请参考以下文章