洛谷 P3705 [SDOI2017]新生舞会 费用流

题目描述

学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。

有 nn 个男生和 nn 个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴。

Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 a i , j

Cathy还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 b i , j ,表示第i个男生和第j个女生一起跳舞时的不协调程度。

当然,还需要考虑很多其他问题。

Cathy想先用一个程序通过 a i , j b i , j 。求出一种方案,再手动对方案进行微调。

Cathy找到你,希望你帮她写那个程序。

一个方案中有n对舞伴,假设没对舞伴的喜悦程度分别是 a 1 , a 2 , . . . , a n ,假设每对舞伴的不协调程度分别是 b 1 , b 2 , . . . , b 。令

C = a 1 + a 2 + . . . + a n b 1 + b 2 + . . . + b n

Cathy希望C值最大。

输入输出格式

输入格式:
第一行一个整数n。

接下来n行,每行n个整数,第i行第j个数表示 a i , j

接下来n行,每行n个整数,第i行第j个数表示 b i , j

输出格式:
一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等。

输入输出样例

输入样例#1:
3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9
输出样例#1:
5.357143
说明

对于10%的数据, 1 n 5

对于40%的数据, 1 n 18

另有20%的数据, b i , j 1

对于100%的数据, 1 n 100 , 1 a i , j , b i , j <= 10 4

分析:显然可以分数规划一下,然后最大权匹配用费用流跑就可以了。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>

const int maxv=3e4+7;
const int maxn=307;
const double inf=100000000000;

using namespace std;

int n,cnt,flow;
int s,t;
int ls[maxn],vis[maxn],pre[maxn];
double a[maxn][maxn],b[maxn][maxn];
double ans,l,r,mid,cost;
double dis[maxn];

queue <int> q;

struct edge{
    int x,y,w,op,next;
    double c;
}g[maxv];

void add(int x,int y,int w,double c)
{
    g[++cnt].x=x; g[cnt].y=y; g[cnt].w=w; g[cnt].c=c;
    g[cnt].next=ls[x]; g[cnt].op=cnt+1; ls[x]=cnt;
    g[++cnt].x=y; g[cnt].y=x; g[cnt].w=0; g[cnt].c=-c;
    g[cnt].next=ls[y]; g[cnt].op=cnt-1; ls[y]=cnt;
}

bool spfa()
{
    while (!q.empty()) q.pop();
    for (int i=s;i<=t;i++)
    {
        dis[i]=-inf;
        vis[i]=0;
    }
    dis[s]=0;
    q.push(s);
    vis[s]=1;
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        for (int i=ls[x];i>0;i=g[i].next)
        {
            int y=g[i].y;
            if ((g[i].w) && (dis[y]<dis[x]+g[i].c))
            {
                dis[y]=dis[x]+g[i].c;
                pre[y]=i;
                if (!vis[y])
                {
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
        vis[x]=0;
    }
    if (dis[t]!=-inf) return 1;
    return 0;
}

void mcf()
{
    int i=t,x=1e9;
    while (i!=s)
    {
        x=min(x,g[pre[i]].w);
        i=g[pre[i]].x;
    }
    flow+=x;
    i=t;
    while (i!=s)
    {
        g[pre[i]].w-=x;
        g[g[pre[i]].op].w+=x;
        cost+=g[pre[i]].c*x;
        i=g[pre[i]].x;
    }
}

int check(double mid)
{
    for (int i=1;i<=cnt;i+=2)
    {
        g[i].w=1;
        g[i+1].w=0;
        if ((g[i].x!=s) && (g[i].y!=t))
        {
            int u=g[i].x,v=g[i].y-n;
            g[i].c=(double)a[u][v]-mid*b[u][v];
            g[i+1].c=-g[i].c;
        }
    }       
    cost=0;     
    while (spfa()) mcf();
    if (cost>=0) return 1;
    return 0;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++) scanf("%lf",&a[i][j]);
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++) scanf("%lf",&b[i][j]);
    }
    s=0; t=2*n+1;
    l=0; r=10000;
    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,0);
        }
    }   
    while (r-l>1e-7)
    {
        mid=(l+r)/2;     
        if (check(mid)) ans=mid,l=mid;
                   else r=mid; 
    }
    printf("%.6lf",ans);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/80750322