二分图相关知识(来自kuangbin博客)

二分图匹配(匈牙利算法)

1.一个二分图中的最大匹配数等于这个图中的最小点覆盖数

K?nig定理是一个二分图中很重要的定理,它的意思是,一个二分图中的最大匹配数等于这个图中的最小点覆盖数。如果你还不知道什么是最小点覆盖,我也在这里说一下:假如选了一个点就相当于覆盖了以它为端点的所有边,你需要选择最少的点来覆盖所有的边。

2.最小路径覆盖=最小路径覆盖=|G|-最大匹配数

在一个N*N的有向图中,路径覆盖就是在图中找一些路经,使之覆盖了图中的所有顶点,

且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,

那么恰好可以经过图中的每个顶点一次且仅一次);如果不考虑图中存在回路,那么每每条路径就是一个弱连通子集.

路径覆盖与二分图匹配的关系:最小路径覆盖=|G|-最大匹配数;

3.二分图最大独立集=顶点数-二分图最大匹配

独立集:图中任意两个顶点都不相连的顶点集合。

二分图模板:

模板一:匈牙利算法

/* **************************************************************************
//二分图匹配(匈牙利算法的DFS实现)
//初始化:g[][]两边顶点的划分情况
//建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
//g没有边相连则初始化为0
//uN是匹配左边的顶点数,vN是匹配右边的顶点数
//调用:res=hungary();输出最大匹配数
//优点:适用于稠密图,DFS找增广路,实现简洁易于理解
//时间复杂度:O(VE)
//***************************************************************************/
//顶点编号从0开始的
const int MAXN=510;
int uN,vN;//u,v数目
int g[MAXN][MAXN];
int linker[MAXN];
bool used[MAXN];
bool dfs(int u)//从左边开始找增广路径
{
    int v;
    for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改
      if(g[u][v]&&!used[v])
      {
          used[v]=true;
          if(linker[v]==-1||dfs(linker[v]))
          {//找增广路,反向
              linker[v]=u;
              return true;
          }
      }
    return false;//这个不要忘了,经常忘记这句
}
int hungary()
{
    int res=0;
    int u;
    memset(linker,-1,sizeof(linker));
    for(u=0;u<uN;u++)
    {
        memset(used,0,sizeof(used));
        if(dfs(u)) res++;
    }
    return res;
}
//******************************************************************************/

模板二: Hopcroft-Carp算法

这个算法比匈牙利算法的时间复杂度要小,大数据可以采用这个算法

/* *********************************************
二分图匹配(Hopcroft-Carp的算法)。
初始化:g[][]邻接矩阵
调用:res=MaxMatch();  Nx,Ny要初始化!!!
时间复杂大为 O(V^0.5 E)

适用于数据较大的二分匹配
需要queue头文件
********************************************** */
const int MAXN=3000;
const int INF=1<<28;
int g[MAXN][MAXN],Mx[MAXN],My[MAXN],Nx,Ny;
int dx[MAXN],dy[MAXN],dis;
bool vst[MAXN];
bool searchP()
{
    queue<int>Q;
    dis=INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=0;i<Nx;i++)
        if(Mx[i]==-1)
        {
            Q.push(i);
            dx[i]=0;
        }
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        if(dx[u]>dis)  break;
        for(int v=0;v<Ny;v++)
            if(g[u][v]&&dy[v]==-1)
            {
                dy[v]=dx[u]+1;
                if(My[v]==-1)  dis=dy[v];
                else
                {
                    dx[My[v]]=dy[v]+1;
                    Q.push(My[v]);
                }
            }
    }
    return dis!=INF;
}
bool DFS(int u)
{
    for(int v=0;v<Ny;v++)
       if(!vst[v]&&g[u][v]&&dy[v]==dx[u]+1)
       {
           vst[v]=1;
           if(My[v]!=-1&&dy[v]==dis) continue;
           if(My[v]==-1||DFS(My[v]))
           {
               My[v]=u;
               Mx[u]=v;
               return 1;
           }
       }
    return 0;
}
int MaxMatch()
{
    int res=0;
    memset(Mx,-1,sizeof(Mx));
    memset(My,-1,sizeof(My));
    while(searchP())
    {
        memset(vst,0,sizeof(vst));
        for(int i=0;i<Nx;i++)
          if(Mx[i]==-1&&DFS(i))  res++;
    }
    return res;
}
//**************************************************************************/

下面的程序效率很高。是用vector实现邻接表的匈牙利算法。

处理点比较多的效率很高。1500的点都没有问题

/*

HDU 1054

用STL中的vector建立邻接表实现匈牙利算法

效率比较高

 G++  578ms  580K

*/

#include<stdio.h>

#include<iostream>

#include<algorithm>

#include<string.h>

#include<vector>

using namespace std;

//************************************************

const int MAXN=1505;//这个值要超过两边个数的较大者,因为有linker

int linker[MAXN];

bool used[MAXN];

vector<int>map[MAXN];

int uN;

bool dfs(int u)

{

    for(int i=0;i<map[u].size();i++)

    {

        if(!used[map[u][i]])

        {

            used[map[u][i]]=true;

            if(linker[map[u][i]]==-1||dfs(linker[map[u][i]]))

            {

                linker[map[u][i]]=u;

                return true;

            }

        }

    }

    return false;

}

inthungary()

{

    int u;

    int res=0;

    memset(linker,-1,sizeof(linker));

    for(u=0;u<uN;u++)

    {

        memset(used,false,sizeof(used));

        if(dfs(u)) res++;

    }

    return res;

}

//*****************************************************

int main()

{

    int u,k,v;

    int n;

    while(scanf("%d",&n)!=EOF)

    {

        for(int i=0;i<MAXN;i++)

           map[i].clear();

        for(int i=0;i<n;i++)

        {

            scanf("%d:(%d)",&u,&k);

            while(k--)

            {

                scanf("%d",&v);

                map[u].push_back(v);

                map[v].push_back(u);

            }

        }

        uN=n;

        printf("%d\n",hungary()/2);

    }

    return 0;

}
时间: 2024-11-29 10:22:45

二分图相关知识(来自kuangbin博客)的相关文章

面试基础知识准备,博客汇总

面试常见设计模式(以重要顺序排) Tips:需要准备一个问题的回答:你用过哪些设计模式,怎么用的? ----(我当时一般都说是在桌面应用开发中用到了单例模式.点击某个按钮,弹出一个窗口进行信息交互.为了用户体验,弹出的窗口不能一直在最上层显示,但又不想多次点击按钮生成多个同样窗口,希望点击一次后,用户操作了其他页面,重复点击该按钮,会将第一次弹出的窗口置于最上层显示而不是再次新建一个该窗口弹出.实现方法是给按钮所在页面类赋一个public全局变量,初次点击按钮时,新建窗口类赋值给该变量.再次点击

博客在信息化教育中的作用及探讨

引言 信息化教育是一种全新的教育形态,它具有资源全球化.学习自主化.活动合作化.环境虚拟化等显著特点,它要求教师教育思想.教育观念的更新,它强调人的发展,为人的发展创造理想的环境,注重培养具有高信息素养能力的创新性人才,Blog的出现并在信息化教育中能够迅速流行正是迎合了当前信息化教育的这些需求并影响着当前信息化教育的发展. 1.博客(Blog)的发展 1.1博客的兴起 博客,由约翰•巴杰在1997年12月提出,英文名为Blog(同义词有web log,weBlog),它的最普通的定义是:一种表

Android应用开发-小巫CSDN博客客户端开发开篇

Android应用开发-小巫CSDN博客客户端开发开篇 2014年9月8日 八月十五 祝各位中秋节快乐 小巫断断续续花了几个星期的时间开发了这么一款应用--小巫CSDN博客,属于私人定制的这样的一款应用,整个客户端的数据全部来自本人博客,是通过爬取本人博客地址html页面,然后解析html把数据提取出来,整个客户端的技术难点主要是如何对html界面进行分析和使用Jsoup对html代码进行解析.目前本人的这款应用已经开发出来了,近段时间会提交应用商店进行审核,不久大家就可以看到这么一款逼格满满的

[转]有哪些值得关注的技术博客(Java篇)

有哪些值得关注的技术博客(Java篇) 大部分程序员在自学的道路上不知道走了多少坑,这个视频那个网站搞得自己晕头转向.对我个人来说我平常在学习的过程中喜欢看一些教程式的博客.这些博客的特点: 1.总结知识点相对比较全面 2.一般来说讲解知识点通俗易懂 3.路线比较清晰,不会有太多的冗余内容. 这样一来,对于自学的朋友来说,一些专业的博客不但大量的缩减了你得学习时间.提高了学习效率.更重要的是这些博客能培养你对编程的兴趣. 于是,这几期文章会主要推荐一些技术博客,目前计划打算主要分为: (已完) 

【转】博客美化(6)为你的博文自动添加目录

[分享]博客美化(6)为你的博文自动添加目录 阅读目录 1.目录样式文件 2.为文章添加固定的信息 3.自动生成目录代码 博客园美化相关文章目录:博客园博客美化相关文章目录 这篇文章使用的代码来自于博客园的marvin,非常感谢.在他的文章:如何给你的为知笔记添加一个漂亮的导航目录中,介绍了给文章自动添加目录的过程,我当时就非常兴奋,一直想要个类似的插件,就是不会写.所以当天就咨询了marvin,在他的提示下,我把他博客的CSS和js文件扒下来了,经过一番改进,成为了我博客目前使用的,所以分享出

【程序人生】写博客一周年纪——横空出世

前言 “吾日三省吾身”,<论语>如是说:“我是靠总结经验吃饭的”,毛主席如是说:“做人要像竹子一样,每攀登一步,就做一次小结”,名人亦如是说.可见,总结对于人生的成长有着多么重要的作用.我等凡人做不到曾子“日三省”,但一年总结一次还是可以办到的.从去年的6月份开始正式写博客,到现在不知不觉已经一年了,正好6月又是一个大考月,也是对过去一个阶段的学业总结,这里就对过去一年写博客的经历做一些总结吧. 一.为什么要写博客 至于我写博客的初衷,这和我工作经历和面试经历有关.这里我想简单回忆一下过去7年

文顶顶iOS开发博客链接整理及部分项目源代码下载

文顶顶iOS开发博客链接整理及部分项目源代码下载 网上的iOS开发的教程很多,但是像cnblogs博主文顶顶的博客这样内容图文并茂,代码齐全,示例经典,原理也有阐述,覆盖面宽广,自成系统的系列教程却很难找.如果你是初学者,在学习了斯坦福iOS7公开课和跟着文顶顶的博客做项目之后,最快只需要2个月时间,就基本可以独立完成iOS App的开发工作.有经验的开发者也可以在该博客中寻找代码片段进行学习借鉴,必有所收获. 在此也向@文顶顶 表示严重感谢! 由于文顶顶博客博文繁多,每次找文章需要频繁的翻页,

【转】Android Building System 总结 - 一醉千年 - CSDN博客

原文网址:http://www.360doc.com/content/15/0314/23/1709014_455175716.shtml Android Building System 总结 收藏 花了一个月的时间来看Android Make,在网上总是看到某某大虾说一天就把Android Make overview了一下,不得不感叹现在大虾的强大和咱那连蜗牛都可以鄙视一下的进度.不过总算是彻底看清的Android make这个系统,不得不当初架构出这套机制的神人顶礼膜拜一下,虔诚地烧三柱高香

Java 开源博客——B3log Solo 0.6.7 正式版发布了!

Java 开源博客 -- B3log Solo 0.6.7 正式版发布了!欢迎大家下载. 另外,欢迎观摩 B3log 团队的新项目:Wide,也非常欢迎大家参与进来 :-) 特性 基于标签的文章分类 Ping Google Blog Search Engine 博客/标签 Atom/RSS 输出 Sitemap 输出 评论回复及邮件提醒 自定义页面 置顶/相关/随机/站外相关文章 文章.页面永久链接(Permalink) 文章草稿夹.签名档.更新提示 缓存管理 多用户 多语言 换肤 插件 Met