• 「SDOI 2017」新生舞会


    题目链接

    戳我

    (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);
    }
    
    
  • 相关阅读:
    【Spring Framework】10、代理模式
    【Spring Framework】8、使用注解开发
    Codeforces 516E
    AtCoder Grand Contest 055 题解
    Codeforces 1606F
    贪心/构造/DP 杂题选做
    整数拆分最大乘积
    CSP-S2021 被碾压记
    洛谷 P2791
    LCT 小记
  • 原文地址:https://www.cnblogs.com/hbxblog/p/10266038.html
Copyright © 2020-2023  润新知