黑海造船厂二分图匹配项目试验顺利进行

什么是二分图?

二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。------摘自百度百科

怎样判定二分图?

以下内容摘自《算法竞赛进阶指南》

存在如下定理:

一张无向图是二分图,当且仅当图中不存在奇环(长度为奇数的环).

根据该定理,同志们可以以染色法进行二分图的判定.主体思想为:尝试用黑白两种颜色标记图中的结点,当一个结点被标记后,它的所有相邻结点应该被标记与它相反的颜色.若标记过程中产生冲突,则说明图中存在奇环.二分图染色一般基于DFS实现,时间复杂度为\(O(n+m)\),伪代码如下:


void dfs(int x,int color)
{
    赋值v[x] <-- color
    对于与x相连的每条无向边(x,y)
    if v[y]=0 then
        dfs(y,3-color)
    else if v[y]=color then
        判定无向图不是二分图,算法结束
}

在主函数中
    for i <-- 1 to n
        if(v[i]=0)then dfs(i,1)
    判定无向图是二分图

二分图最大匹配

以下定义摘自《算法竞赛进阶指南》

"任意两条边没有公共端点"的边的集合被称为图的一组匹配.在二分图中,包含边数最多的一组匹配被称为二分图的最大匹配.

对于任意一组匹配\(S\)(\(S\)是边集),属于\(S\)的边被称为"匹配边",不属于\(S\)的边被称为"非匹配边".匹配边的端点被称为"匹配点",其他结点被称为"匹配点".如果二分图中存在一条连接两个非匹配点的路径\(path\),使得非匹配边与匹配边在\(path\)上交错出现,那么称\(path\)是匹配\(S\)的增广路,也称交错路.

有如下定理:

二分图的一组匹配\(S\)是最大匹配,当且仅当图中不存在\(S\)的增广路.

配图如下:

如图,图2,图3(请不要联想到图波耶夫设计局)中红色的边就是图1的匹配,图3中红色的边为图1的最大匹配.

匈牙利算法(增广路算法)

匈牙利算法,又称增广路算法,用于计算二分图最大匹配.

引入

独立团战士们要发枪,每个战士都有自己想要的枪.关系如下:

开始分配:

政委想要捷克式轻机枪,于是把捷克式轻机枪分给了政委.

团长这时说道:"好你他娘的赵政委,当几天政委还蹭鼻子上脸了是吧?老子就要用捷克式轻机枪."

政委想了想,反正咱百里赵刚用汉阳造也顺手,顺手拿来了汉阳造,轻机枪给了团长.

但是和尚也想用这把汉阳造,于是找政委商量.

政委:"关心小同志是咱当政委的职责,要不这样,这把汉阳造就拿给你用,我去找团长商量用轻机枪."

团长:"怎么?又打老子的轻机枪的主意?我看你和老子还有几天交情,轻机枪给你用,行了吧,读书人还真他娘的不讲理.这几天闲着没事,二营的那台意大利炮,老子拿来玩几天."

于是团长从二营拖来了意大利炮,政委用上了轻机枪,和尚拿到了汉阳造.

二营长这就不乐意了,我堂堂张大彪,好不容易缴获的意大利炮,哪是个团长随随便便就能拿来玩的?

于是张大彪找到了团长.

团长:"去你他娘的二营长,老子官比你大,用你的炮怎么啦?老子把炮给你了老子用什么打仗?你让那小鬼子自个往我嘴里钻?我看你打仗天天摔帽子,那顶上次去边区领多了一顶帽子,你给老子拿去用."

于是张大彪分到了一顶帽子.

主体思想

上面的过程就是一个标准的匈牙利算法.

匈牙利算法的思想就是寻找增广路,把增广路上的匹配状态全部取反,得到一个更大的匹配.

具体来说的话,可以以上面的第二幅图举例.

"李团长 --> 捷克式轻机枪 --> 赵政委 --> 汉阳造",这是匹配"赵政委 --> 捷克式轻机枪"的一条增广路,其中"李团长"和"汉阳造"为增广路连接的两个非匹配点.同志们把这条增广路上的所有边的匹配状态取反,原来政委拿到了轻机枪,取反之后变为没有拿到,原来团长没有拿到轻机枪,取反后拿到了轻机枪,原来政委没有拿到汉阳造,取反后拿到了汉阳造.

这样就得到了一个更大的匹配.

我们重复第二步,直到图中不存在增广路,就得到了最大匹配.

因为该算法最多遍历整个二分图一次,所以时间复杂度为\(O(nm)\).

具体实现

  1. 设\(S\)为空集,即所有的边都是非匹配边.
  2. 寻找增广路,把路径上的边全部取反,得到一个更大的匹配\(S`\)
  3. 重复第二部,直到图中不存在增广路.

关于具体怎么寻找增广路,怎么给路径取反,我讲在代码中解释.

模板题

代码:


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

struct edge
{
    int to,next;
}e[1000010];

int n,m,e_,size,ans;
int head[10010],match[10010];
bool flag[10010];

inline void EdgeAdd(int,int);
inline void Hungary();
inline bool find(int);

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d%d",&n,&m,&e_);
    for(int _=1;_<=e_;_++)
    {
        int from,to;
        scanf("%d%d",&from,&to);
        if(from>n||to>m||from>m||to>n)
        {
            continue;
        }
        EdgeAdd(from,to);
    }
    Hungary();
    printf("%d\n",ans);
return 0;
}

inline void EdgeAdd(int from,int to)
{
    e[++size].to=to;
    e[size].next=head[from];
    head[from]=size;
}

inline void Hungary()
{
    for(int _=1;_<=n;_++)//枚举左集中的结点
    {
        memset(flag,false,sizeof(flag));
        if(find(_)==true)//存在一条增广路
        {
            ans++;
        }
    }
}

inline bool find(int from)
{
    for(int _=head[from];_!=-1;_=e[_].next)//遍历该结点的路径
    {
        int to=e[_].to;
        if(flag[to]==false)//flag数组防止往回走
        {
            flag[to]=true;
            if(match[to]==0||find(match[to])==true)
/*这里有两种情况,一种是右集中的点没有被选,那么它们俩构成长度为1的增广路.
另一种是右集中的点已经被选了,但是往下递归可以发现选它的点可以有其他的选择,这样构成了一条两端都是非匹配点的路径,即增广路.
*/
            {
                match[to]=from;//更改匹配
                return true;
            }
        }
    }
return false;
}

原文地址:https://www.cnblogs.com/Lemir3/p/11113535.html

时间: 2024-11-11 22:49:03

黑海造船厂二分图匹配项目试验顺利进行的相关文章

BZOJ 1191: [HNOI2006]超级英雄Hero(二分图匹配)

云神说他二分图匹配从来都是用网络流水过去的...我要发扬他的精神.. 这道题明显是二分图匹配.网络流的话可以二分答案+最大流.虽然跑得很慢.... ---------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostrea

【二分图匹配/匈牙利算法】飞行员配对方案问题

P2756 飞行员配对方案问题 确认过眼神, 是二分图匹配板子题啦!!! 跑个匈牙利, 有匹配的输出, 记得先输出外籍飞行员, 因为有spj顺序无所谓啦qwq 最近A的最顺利的题了哈哈哈哈哈哈开心!!!!!!!! 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 const int sz = 100010; 6 int n, m, num = 0, a

POJ2584 T-Shirt Gumbo 二分图匹配(网络流)

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 const int inf=0x3f3f3f3f; 6 const int sink=30; 7 8 struct Edge 9 { 10 int to; 11 int next; 12 int capacity; 13 14 void assign(int t,int n,int c) 15 { 16 to=t; next=n; ca

棋盘游戏(二分图匹配)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1281 棋盘游戏 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3200    Accepted Submission(s): 1897 Problem Description 小 希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放

BZOJ 1854 游戏(二分图匹配或并查集)

此题的二分图匹配做法很容易想,就是把属性当做s集,武器当做t集,如果该武器拥有该武器则连一条边. 那么答案就是求该二分图的最大前i个匹配.将匈牙利算法改一改,当前找不到增广路就break. 但是过这个题需要常数优化,不能每次都fillchar一遍used数组.可以用队列将使用的used点加入,然后需要初始化的时候弹出即可. # include <cstdio> # include <cstring> # include <cstdlib> # include <i

HDU 3081:Marriage Match II(二分图匹配+并查集)

http://acm.hdu.edu.cn/showproblem.php?pid=3081 题意:有n个男生n个女生,他们只有没有争吵或者女生a与男生A没有争吵,且女生b与女生a是朋友,因此女生b也可以和男生A过家家(具有传递性).给出m个关系,代表女生a和男生b没有争吵过.给出k个关系,代表女生a与女生b是好朋友.每一轮过家家之后,女生只能选择可以选择并且没选过的男生过家家,问游戏能进行几轮. 思路:因为n<=100,因此支持O(n^3)的算法,挺容易想到是一个二分图匹配的.(出现在我的网络

11082 - Matrix Decompressing (网络流建模|二分图匹配)

该题是一道经典的二分图匹配的题目 .现在终于有点明白什么是二分图匹配了,其实说白了就是依赖于最大流算法之上的一种解决特定问题的算法 . 所谓二分图,就是我们假定有两个集合A和B,每个集合中有若干元素(点),其中源点与A相连,汇点与B相连,并且他们的总容量决定了最终答案的上限,所以一定要维护好 . 然后由A中的点向B中的点连线,他们之间也有一定的容量制约关系(具体看题目中的边权值限制).这样就可以求出最大流量匹配了. 有时我们要求完美匹配,即所有流入的量等于流出的量  . 该题构思极其巧妙,因为我

二分图匹配(指派问题)

指派问题: 有N台计算机和K个任务,我们可以给每台计算机分配一个任务,每台计算机能够处理的任务种类不同,请求出最多能够处理的任务的个数. 思路:二分图匹配,可以这样来定义无向二分图,G=(UuV,E); U 代表计算机的顶点集合,V代表任务的顶点集合,对于任意u属于U和v属于V,计算机u能够处理的任务v<=>(u,v)属于E 二分图例子: 对原图做如下改变: 将原图中所有无向边e改为有向边,方向从U到V,容量1,增加源点s和汇点t,从s向所有的顶点u属于U连一条容量为1的边,从所有的顶点V属于

算法模板——二分图匹配

实现功能为二分图匹配 本程序以Codevs2776为例 详见Codevs2776 1 type 2 point=^node; 3 node=record 4 g:longint; 5 next:point; 6 end; 7 var 8 i,j,k,l,m,n:longint; 9 c,f:array[0..1000] of longint; 10 a:array[0..1000] of point; 11 procedure add(x,y:longint);inline; 12 var p: