P3705 [SDOI2017]新生舞会 分数规划+费用流

题意:

戳这里

分析:

模拟赛出了这道题的弱化版,把矩阵从 \(n^2\) 变成了 \(2\times n\) 的,被巨佬们用模拟退火退过去了。。。

现在我们考虑正解,题意等价于,从矩阵中选出 \(n\) 个点,满足任意两个点不属于同一行或列,求 \(\frac{\sum a}{\sum b}\) 的最大值

我们发现这个式子长得很分数规划,所以我们化简得到 \(\sum a-ans*\sum b=0\) ,也就是说矩形内部每一个点的权值变成了 \(a-ans\times b\) 然后每行每列选一个点,使所有点的权值之和最大,既要满足个数要求和限制条件,同时还要求出最大代价,我们发现这不就是费用流

我们按照分数规划二分得到的值,将行 \(i\) 和列 \(j\) 之间连一条流量为 \(1\) 费用为 \(ans*b[i][j]-a[i][j]\) 的边,然后原点和每一行连一条流量为 \(1\) 费用为 \(0\) 的边,每一列和汇点连一条流量为 \(1\) 费用为 \(0\) 的边,这样直接跑网络流,我们就能保证最大流的情况下得到 \(\sum b*ans-\sum a\) 的最小值 (等价于 \(\sum a-ans\times \sum b\) 的最大值)

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }
    
    const double eps = 1e-8;
    const int maxn = 205;
    const int maxm = 1e5+5;
    int n,cnt=1,st,ed;
    int a[maxn][maxn],b[maxn][maxn];
    int head[maxn],lst[maxn],pre[maxn],flow[maxn];
    bool vis[maxn];
	double dis[maxn];
    queue<int> q;

    struct edge
    {
        int to,nxt,w;
        double cst;
    }e[maxm];

    void add(int u,int v,double w,int x)
    {
        e[++cnt].to=v;
        e[cnt].cst=w;
        e[cnt].w=x;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }

    bool spfa()
	{
		for(int i=st;i<=ed;i++) pre[i]=-1,vis[i]=false,dis[i]=1000000.0,flow[i]=0x3f3f3f3f;
		dis[st]=0;
		q.push(st);
		while(!q.empty())
		{
			int u=q.front();q.pop();
			vis[u]=false;
			for(int i=head[u];i;i=e[i].nxt)
			{
				int v=e[i].to;
				if(e[i].w&&dis[v]>dis[u]+e[i].cst)
				{
					dis[v]=dis[u]+e[i].cst;
					pre[v]=u;
					lst[v]=i;
					flow[v]=min(flow[u],e[i].w);
					if(!vis[v])
					{
						vis[v]=true;
						q.push(v);
					}
				}
			}
		}
		return pre[ed]!=-1;
	}
	
	double MCMF()
	{
		double res=0;
		while(spfa())
		{
			int now=ed;
			res+=dis[now]*flow[now];
			while(now!=st)
			{
				e[lst[now]].w-=flow[ed];
				e[lst[now]^1].w+=flow[ed];
				now=pre[now];
			}
		}
		return res;
	}

    bool check(double x)
    {
        cnt=1;memset(head,0,sizeof(head));
        for(int i=1;i<=n;i++)
        {
            add(st,i,0,1);add(i,st,0,0);
            add(i+n,ed,0,1);add(ed,n+i,0,0);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                add(i,j+n,x*b[i][j]-a[i][j],1);add(j+n,i,-x*b[i][j]+a[i][j],0);
            }
        }
        return MCMF()<0;
    }

    void solve()
    {
        double l=0,mid,r=1000000.0,ans;
        while(r-l>eps)
        {
            mid=(l+r)/2.0;
            if(check(mid))
            {
                ans=mid;
                l=mid+eps;
            }
            else r=mid-eps;
        }
        printf("%.6f\n",ans);
    }

    void init()
    {
        n=read();st=0;ed=n*2+1;
        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();
    }

    void work()
    {
        init();
        solve();
    }

}


int main()
{
    zzc::work();
    return 0;
}
posted @ 2020-12-29 23:53  youth518  阅读(71)  评论(0编辑  收藏  举报