首页 > 其他分享 >[SDOI2017] 新生舞会——二分 最大费用最大流

[SDOI2017] 新生舞会——二分 最大费用最大流

时间:2024-10-13 16:11:09浏览次数:6  
标签:二分 舞会 le 舞伴 mid SDOI2017 Cathy

[SDOI2017] 新生舞会

题目描述

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

有 \(n\) 个男生和 \(n\) 个女生参加舞会,一个男生和一个女生一起跳舞,互为舞伴。

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'_n\)。令

\(C=\frac {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

样例输入 #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\le n\le 5\)

对于 40% 的数据,\(1\le n\le 18\)

另有 20% 的数据,\(b_{i,j}\le 1\)

对于 100% 的数据,\(1\le n\le 100,1\le a_{i,j},b_{i,j}\le10^4\)

分析

二分C值,用最大费用最大流检查是否满足要求即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
long long INF=0x7fffffff;
struct edge{int y,n,x,z;double sp;}e[N<<1],t[N<<1];
double eps=0.00000001;
int vis[N],dfn,q[N],pre[N];
int n,sta,edn;
int head[N],cnt=1;
int a[210][210],b[210][210];
double dis[N];
void ad(int x,int y,int z)
{
    e[++cnt].n=head[x];
    e[cnt].y=y;
    e[cnt].x=x;
    e[cnt].z=z;
    head[x]=cnt;
}
void init()
{
    INF*=INF;
    scanf("%d",&n);
    sta=n*2+1;
    edn=n*2+2;
    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]);
    for(int i=1;i<=n;++i)
    {
        ad(sta,i,1);
        ad(i,sta,0);
        ad(i+n,edn,1);
        ad(edn,i+n,0);
        for(int j=1;j<=n;++j)
        {
            ad(i,j+n,1);
            ad(j+n,i,0);
        }
    }
}
void ISAP()
{
    dis[0]=-INF;
    for(int i=1;i<=edn;++i)
        dis[i]=-INF;
    dis[sta]=0;
    ++dfn;

    int he=1,ta=0;
    q[++ta]=sta;
    while(he<=ta)//break the limit
    {
        int u=q[he++];
        vis[u]=dfn-1;

        for(int i=head[u];i;i=e[i].n)
        {
            int v=e[i].y;
            if(t[i].z>0 && dis[v]<dis[u]+t[i].sp)
            {
                dis[v]=dis[u]+t[i].sp;
                pre[v]=i;
                if(vis[v]!=dfn)
                {
                    q[++ta]=v;
                    vis[v]=dfn;
                }
            }
        }
    }
}
double returnflow()
{
    int u=edn,mxflow=114514;
    double spe=0;
    while(u!=sta)
    {
        mxflow=min(mxflow,t[pre[u]].z);
        u=e[pre[u]].x;
    }
    u=edn;
    while(u!=sta)
    {
        spe+=t[pre[u]].sp*mxflow;
        t[pre[u]].z-=mxflow;
        t[pre[u]^1].z+=mxflow;
        u=e[pre[u]].x;
    }
    return spe;
}
bool check(double lim)
{
    for(int i=2;i<=cnt;i+=2)
    {
        t[i].z=e[i].z;
        t[i^1].z=e[i^1].z;
        int x=e[i].x,y=e[i].y;
        if(x>n && x<=n*2)x-=n;
        if(y>n && y<=n*2)y-=n;
        double num=1.0*a[x][y]-1.0*lim*b[x][y];
        t[i].sp=num;
        t[i^1].sp=-num;
    }
    double mxflow=0;
    while(1)
    {
        ISAP();
        if(dis[edn]==dis[0])break;
        mxflow+=returnflow();
    }
    return mxflow>=0;
}
void work()
{
    double L=0,R=1e8+1000,mid=0,ans=-1;
    while(R-L>=eps)
    {
        mid=(L+R)/2;
        if(check(mid)){ans=mid;L=mid;}
        else R=mid;
    }
    printf("%.6lf",ans);//too small
}
int main()
{
    init();
    work();
    return 0;
}









标签:二分,舞会,le,舞伴,mid,SDOI2017,Cathy
From: https://www.cnblogs.com/Glowingfire/p/18462486

相关文章

  • C#二分查找算法
    前言二分查找算法是一种在有序数组中查找特定元素的搜索算法。实现原理二分查找的实现依赖于以下几个关键步骤:计算查找范围的中间索引。比较中间索引处的值与目标值。根据比较结果调整查找范围(左半部分或右半部分)。重复上述步骤直到找到目标值或查找范围为空。动图演示......
  • 整体二分 学习笔记
    整体二分本文通过介绍几道例题的解法,带你深入了解整体二分的精髓。例题大致按难度排序,其中,中间的三道题都是类似的。P3527[POI2011]MET-MeteorsP3332[ZJOI2013]K大数查询P2617DynamicRankingsP1527[国家集训队]矩阵乘法P5163WD与地图简介给你\(Q\)个询问,每......
  • ### 100th 2024/9/8 WQS二分小结
    破百了,路长了这个世界,能听见我的回响吗?循环了很久很久的Echoism回望了过去,也要认真注视当下的现实了对吗?来看看WQS二分可以用上的题目有Raper,Gmoj的coffee和划分序列这几题都有一个共同的特点,就是要从n个中恰好选k个的极值而他们的取值都有一个共性,就是关于k,该函数的形状......
  • 二分图全面学习笔记
    二分图全面学习笔记Part1:二分图的定义与判定方法首先,我们要知道二分图的定义是什么。二分图的定义​ 如果一张无向图的\(n\)个节点可以分成\(A,B\)两个不相交的非空集合,并且同一个集合之中的两个点之间没有边相连接,那么称该无向图为二分图(BipartiteGraph)举个栗子很......
  • 二分
    眼红的Medusa题目描述虽然MissMedusa到了北京,领了科技创新奖,但是她还是觉得不满意。原因是:他发现很多人都和她一样获了科技创新奖,特别是其中的某些人,还获得了另一个奖项——特殊贡献奖。而越多的人获得了两个奖项,MissMedusa就会越眼红。于是她决定统计有哪些人获得了两个奖......
  • 深度理解二分查找思想~
    二分思想的基本思想:是将有序数组分成两半,通过比较数组中间元素与目标值大小,来决定下一步搜索的区间是左半部分还是右半部分,然后递归再选定的区间内继续查找。(部分有序的数组也可使用这一思想)二分查找基础代码根据划分区间方法的不同,主要分为三种类型(并在代码后方附有......
  • 二分图最大匹配-匈牙利算法
    二分图最大匹配设G为二分图,若在G的子图M中,任意两条边都没有公共节点,那么称M为二分图G的一组匹配。在二分图中,包含边数最多的一组匹配称为二分图的最大匹配。交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。增广路:从一个未匹配点......
  • 二分答案法
    二分答案法估计最终答案的大概范围分析问题的答案和给定条件之间的单调性建立一个f函数,当答案固定的情况下,判断给定的条件是否达标在最终答案可能的范围上不断二分搜索,每次用f函数判断,直到二分结束,找到最合适的答案875.爱吃香蕉的珂珂#include<vector>#inc......
  • 二分查找算法
    二分查找算法基本思路二分查找的基本思路如下:找到中间元素:查找过程从数组的中间元素开始,如果中间元素恰好是目标元素,则查找过程结束。比较并缩小范围:如果中间元素不是目标元素,那么将中间元素与目标值进行比较:如果目标值小于中间元素,则说明目标值位于当前搜索范围的左半部分......
  • 二分搜索与二分答案
    二分前提条件数组的有序的数组数组中无重复元素,否则查找的元素下标可以不算唯一的二分答案二分答案时需要满足答案的有界性二分答案的思路:首先判断这个解是否为可行解,然后我们”认为“这个可行解的最优解,然后以这个可行解为标准去左(右)区间寻找最优解时间复杂......