二分图匹配

定义:

  • 二分图:一个图被分成了两部分,相同的部分没有边

  • 匹配:二分图G的子图M中,M的边集{E}中的任意两条边都不指向同一个顶点

  • 极大匹配:在当前已完成的匹配下,无法再通过增加未完成匹配的边的方式来增加匹配的边数

  • 最大匹配:是所有极大匹配当中边数最大的一个匹配

  • 完全匹配(完备匹配):一个匹配中,图中的每个顶点都和图中某条边相关联

有什么用:

  • 定理1:最小点覆盖数 = 最大匹配数

    点覆盖:点集合使得任意一条边至少有一个端点在集合中

  • 定理2:最大独立集 = 顶点数 - 最大匹配数

    独立集:点集合中任何两个顶点都不互相连接

  • 定理3:最小路径覆盖数 = 顶点数 – 最大匹配数

    路径覆盖:任何一个点都属于且仅属于一条路径

代码实现:(匈牙利算法)
匈牙利算法与交错路

#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
vector<int> g[1005];
int n,m,e;
int link[1005],cnt;
int vis[1005];
int find(int x){for(int i=0;i<g[x].size();i++){int u=g[x][i];if(!vis[u]){vis[u]=1;if(link[u]==0||find(link[u])){link[u]=x;return 1;}}}return 0;
}
int main(){cin>>n>>m>>e;int x,y;for(int i=1;i<=e;i++){cin>>x>>y;if (x>=1&&y>=1&&x<=n&&y<=m)g[x].push_back(y);}for(int i=1;i<=n;i++){memset(vis,0,sizeof(vis));if(find(i)) cnt++;}cout<<cnt;return 0;
}

时间复杂度:

O(nm) 其中n是点数,m是边数

但事实上,对于绝大部分的二分图,匈牙利算法都跑不够上限

KM算法

题目:

最小点覆盖数:

定理证明:http://www.cnblogs.com/jasonlixuetao/p/4756026.html

POJ 3041

题意:有一个N*N的网格,该网格有K个障碍物.你有一把武器,每次你使用武器可以清楚该网格特定行或列的所有障碍.问你最少需要使用多少次武器能清除网格的所有障碍物?

/*把网格的行1到N看成左边点集的点,网格的列号看成右边点集的点
如果(i,j)格有障碍,那么就在左边i点到右边j点之间连接一条边
接下来求最小点覆盖数即可*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=500+10;
int n;
vector<int> g[maxn];
int vis[maxn];
int link[maxn];
int find(int u) {for(int i=0;i<g[u].size();i++){int v=g[u][i];if(!vis[v]) {vis[v]=1;if(!link[v]||find(link[v])){link[v]=u;return 1;}}}return 0;
}
int main(){int n,k;scanf("%d%d",&n,&k);while(k--) {int u,v;scanf("%d%d",&u,&v);g[u].push_back(v); }int ans=0;for(int i=1;i<=n;i++){memset(vis,0,sizeof(vis));if(find(i)) ans++;}printf("%d\n",ans);return 0;
}
最大独立集:

定理证明:最大独立集=顶点数-最小顶点覆盖数=顶点数-最大匹配数

HDU 3829

题意:动物园有若干只狗和猫,每个小朋友都要么喜欢一只狗不喜欢一只猫,要么反过来。现在要移除一些狗和猫,小朋友只有在不移除自己喜欢的动物,移除了自己不喜欢的动物才会开心。问最多能让几个小朋友开心。

/*把人当作节点,矛盾的人连边,做最大独立集*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=5e2+7;
struct Edge{int v,nxt;Edge(int v=0,int nxt=0):v(v),nxt(nxt){}
}e[N*N];
int n,tmp;
int head[N],cnt;
void add(int u,int v){e[++cnt]=Edge(v,head[u]);head[u]=cnt;e[++cnt]=Edge(u,head[v]);head[v]=cnt;
}
int vis[N],link[N];
bool find(int u){for(int i=head[u];~i;i=e[i].nxt){int v=e[i].v;if(vis[v]) continue;vis[v]=1;if(link[v]==-1||find(link[v])){link[v]=u;return true;}}return false;
}
struct Node{char x[5],y[5];
}a[N];
char s1[5],s2[5];
int main(){int n,m,k;while(scanf("%d%d%d",&n,&m,&k)!=EOF){memset(head,-1,sizeof(head));cnt=-1;memset(link,-1,sizeof(link));for(int i=1;i<=k;i++)scanf("%s%s",a[i].x,a[i].y);for(int i=1;i<=k;i++)for(int j=i+1;j<=k;j++)if(strcmp(a[j].x,a[i].y)==0||strcmp(a[j].y,a[i].x)==0)add(i,j);int ans=0;for(int i=1;i<=k;i++){memset(vis,0,sizeof(vis));if(find(i)) ans++;}printf("%d\n",k-ans/2);}
}
拓展:
二分图的最大团定义:对于一般图来说,团是一个顶点集合,这些顶点两两之间都有边。最大团就是使得选出的这个顶点集合最大补图:一个图G的补图是一个图有着跟G相同的点,而且这些点之间有边相连当且仅当在G里面他们没有边相连。方法:二分图的最大团=补图的最大独立集
最小路径覆盖数:

DAG 的最小不相交路径覆盖

定理证明:

首先,若最大匹配数为0,则二分图中无边,也就是说有向图G中不存在边,那么显然:最小路径覆盖=|G|-最大匹配数=|G|-0=|G|。若此时增加一条匹配边x1--y2,则在有向图|G|中,x、y在同一条路径上,最小路径覆盖数减少一个。继续增加匹配边,每增加一条,最小路径覆盖数减少一个,则公式:最小路径覆盖=|G|-最大匹配数得证。

最小不相交路径覆盖数:

POJ 1422

题意:T组数据,有 N 个村庄 M 条有向路,构成的图不会出现环,现在计划空降多组士兵,每组士兵一个人,士兵可以不回头的走完全程,别的士兵走过的地方,不能再走了。问最少将降落几组士兵可以遍历全部的节点。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=200+10;
vector<int> g[N];
int t,n,m;
int link[N];
int vis[N];
int find(int u){for(int i=0; i<g[u].size(); ++i){int v=g[u][i];if(vis[v]) continue;vis[v]=1;if(!link[v]||find(link[v])){link[v]=u;return 1;}}return 0;
}
int main(){int u,v;scanf("%d",&t);while(t--){scanf("%d%d",&n,&m);memset(link,0,sizeof(link));for(int i=1;i<=n;++i)g[i].clear();for(int i=1;i<=m;++i){scanf("%d%d",&u,&v);g[u].push_back(v);}int ans=0;for(int i=1;i<=n;++i){memset(vis,0,sizeof(vis));ans+=find(i);}printf("%d\n",n-ans);}return 0;
}

最小相交路径覆盖数:

POJ 2594

题意:在一张有向无环图中,放置若干个机器人,每个机器人都有一个属于自己的路径。图中每一个节点都可以属于多个路径。问最少需要投放几个机器人才能遍历完图中每个节点。

/*如果两个点a和b是连通的,只不过中间需要经过其它的点,那么可以在这两个点之间加边
先跑一遍floyd确定哪些点是连通的,然后就转化成了最小不相交路径数问题*/
#include<iostream>
#include<cstring> 
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=5e2+7;
int mp[N][N];
int vis[N],link[N];
int n,m,u,v;
bool find(int u){for(int v=1;v<=n;v++){if(mp[u][v]&&!vis[v]){vis[v]=1;if(link[v]==-1||find(link[v])){link[v]=u;return true;}}}return false;
}
int main(){while(scanf("%d%d",&n,&m)!=EOF){if(n==0&&m==0) break;memset(link,-1,sizeof(link));memset(mp,0,sizeof(mp));for(int i=1;i<=m;i++){scanf("%d%d",&u,&v);mp[u][v]=1;}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(!mp[i][j]) continue;for(int k=1;k<=n;k++){if(!mp[j][k]) continue;mp[i][k]=1; }}}int ans=0;for(int i=1;i<=n;i++){memset(vis,0,sizeof(vis));if(find(i)) ans++;}printf("%d\n",n-ans);}
}
杂题:

HDU 1045

题意:给出一张图,‘X’代表墙,‘.’ 代表空地。在空地上放一些炮塔,炮塔不能处在同一行同一列,除非被墙隔开。问最多能放多少个炮塔。

/*如果不存在墙,把每一列缩成一点,每一行缩成一点进行匹配即可
如果存在墙,墙的两侧其实就相当于是两个互不相关的点
首先按列遍历,对每一列的联通串缩点并染色
然后按行遍历,同样的对联通串缩点,并与所染色的对应列联通串连边*/ 
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e3+7;
struct Edge{int u,v,nxt;
}edge[N<<1];
int n,cnt,head[N],link[N],vis[N];
int col[N][N];
char mp[N][N];
void add(int u,int v){edge[++cnt].u=u;edge[cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt;edge[++cnt].u=v;edge[cnt].v=u;edge[cnt].nxt=head[v];head[v]=cnt;
}
int find(int u){for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(!vis[v]){vis[v]=1;if(link[v]==0||find(link[v])){link[v]=u;return 1;}}}return 0;
}
int main(){while(scanf("%d",&n)!=EOF){if(n==0) break;memset(head,0,sizeof(head));memset(link,0,sizeof(link));cnt=0;for(int i=1;i<=n;i++)scanf("%s",mp[i]+1);int tot=1;for(int j=1;j<=n;j++){for(int i=1;i<=n;i++){if(mp[i][j]=='X') tot++;else col[i][j]=tot;}tot++;}tot=1;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(mp[i][j]=='X') tot++;else add(tot,col[i][j]+100);}tot++;}int ans=0;for(int i=1;i<=tot;i++){memset(vis,0,sizeof(vis));if(find(i)) ans++;}printf("%d\n",ans);}return 0;
} 

HDU 4185

题意:一张n乘n的二维图,由‘#’和‘.’构成。要求用尽可能多的1乘2的矩形去覆盖‘#’,矩形内不能含有‘.’,问最多能覆盖多少个矩形。

注意:这题的n没有题面说的那么大。

/*每一个 #就是一个点,相邻的 #互相连接,然后进行二分匹配即可*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e3+7;
const int go[4][2]={-1,0,1,0,0,1,0,-1};
struct Edge{int u,v,nxt;
}edge[N<<1];
int t,cs,n,cnt,head[N],link[N],vis[N];
int mp[N][N];
char ch[N][N];
void add(int u,int v){edge[++cnt].u=u;edge[cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt;
}
int find(int u){for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(!vis[v]){vis[v]=1;if(link[v]==0||find(link[v])){link[v]=u;return 1;}}}return 0;
}
int main(){scanf("%d",&t);while(t--){cnt=0;memset(head,0,sizeof(head));memset(link,0,sizeof(link));memset(mp,0,sizeof(mp));scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%s",ch[i]+1);int tot=0;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(ch[i][j]=='#')mp[i][j]=++tot;}}for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(mp[i][j]){for(int k=0;k<4;k++){int nx=i+go[k][0];int ny=j+go[k][1];if(mp[nx][ny])add(mp[i][j],mp[nx][ny]);}}}}int ans=0;for(int i=1;i<=tot;i++){memset(vis,0,sizeof(vis));if(find(i)) ans++;}printf("Case %d: %d\n",++cs,ans/2);}return 0;
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/319905.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【整体二分】区间第k小(金牌导航 整体二分-1)

区间第k小 金牌导航 整体二分-1 题目大意 给出一个序列&#xff0c;有若干查询&#xff0c;每次查询给出l,r,k&#xff0c;让你求l~r这个区间的第k大 输入样例 7 3 1 5 2 6 3 7 4 2 5 3 4 4 1 1 7 3输出样例 5 6 3数据范围 1⩽n⩽105,1⩽m⩽50000,1⩽∣ai∣⩽1091\leqsla…

积极参与开源项目,促进.NET Core生态社区发展

今天早上在微信群里聊天聊到百度的SDK 已经支持.NET Core, 百度已经在3月份就支持了&#xff0c;想起当时还是我在他们的github上提的issue&#xff1a; https://github.com/Baidu-AIP/dotnet-sdk/issues/3。.NET Core生态社区的发展已经四年多时间&#xff0c;日趋完善&#x…

P6091-[模板]原根

正题 题目链接:https://www.luogu.com.cn/problem/P6091 题目大意 给出一个数ppp&#xff0c;求出它的所有在[0,p][0,p][0,p]的原根。 解题思路 原根的定义&#xff0c;δp(a)\delta_p(a)δp​(a)表示一个最小的nnn使得an≡1(modp)a^n\equiv1(mod\ p)an≡1(mod p)&#xff0…

并查集小记

有什么用&#xff1a; 查询元素a和元素b是否属于同一组 合并元素a和元素b所在组 代码实现&#xff1a; #include<iostream> using namespace std; int n,m,p; int fa[5001]; int find(int x){if(fa[x]x) return x;else{return fa[x]find(fa[x]);} } int main(){cin&g…

Poj 1011 UVA - 307 Sticks

牛客网 poj 1011 题目&#xff1a; George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long th…

HAPPY2020暑假训练前复习

A.计蒜客 - T1381 输出hello world 万恶之源 B.51Nod - 2060 全排列输出 不要用STL的next_permutation,会超时 #include <bits/stdc.h> using namespace std; const int maxn14; int dt[maxn]; int vis[maxn];int n; void dfs(int depth) {if(depthn){for(int i0;; i…

【LCT】洞穴勘测(luogu 2147/金牌导航 LCT-1)

洞穴勘测 luogu 2147 金牌导航 LCT-1 题目大意 给你若干操作&#xff0c;有三种操作&#xff1a; 1.连接两个点 2.吧两个点之间的连边断掉&#xff08;保证有这条边&#xff09; 3.查询两个点之间是否连通 样例 #1 输入样例 #1 200 5 Query 123 127 Connect 123 127 Que…

Service Fabric 与Ocelot 的集成

概要云应用程序通常都需要使用前端网关&#xff0c;为用户、设备或其他应用程序提供同一个入口点。 在 Service Fabric 中&#xff0c;网关可以是任意无状态服务&#xff08;如 ASP.NET Core 应用程序&#xff09; 。本文介绍了如何将Ocelot用作 Service Fabric 应用程序的网关…

图论复习——最短路

知识点 最短路径算法 最短路径树 每个点uuu的父亲为使uuu得到最短距离的前驱节点&#xff0c;若有多个&#xff0c;则取任意一个。 题目 CF449B Jzzhu and Cities Blog CF464E The Classic Problem Blog [XSY3888] 传送门 对每个点uuu&#xff0c;记d(u)d(u)d(u)表示uuu…

Loj#143-[模板]质数判定【Miller-Rabin】

正题 题目链接:https://loj.ac/p/143 题目大意 给出一个数ppp&#xff0c;让你判定是否为质数。 解题思路 Miller−RabinMiller-RabinMiller−Rabin是一种基于费马小定理和二次探测定理的具有较高正确性的高效质数判定算法。 首先讲一下两个定理 费马小定理&#xff1a;gcd(…

【LCT】Tree II(luogu 1501)

Tree II luogu 1501 题目大意 给出一棵树&#xff0c;让你进行若干操作&#xff0c;操作如下&#xff1a; 1.把两个点路径上的所有点权值加k 2.把两个点路径上的所有点权值乘k 3.把一条边断开&#xff0c;连上另一条边 4.查询两个点路径上的权值和 输入样例 3 2 1 2 2 3 *…

图论复习汇总

三元环计数&四元环计数 Blog dfs树,点双,边双,强连通分量 Blog bfs树 对一个图运行 bfs 算法&#xff0c;每个点uuu的父亲定义为第一次遍历uuu时的前驱结点&#xff0c;若无则为根。 非树边只存在在同一层的两个点和相邻层的点中。 hihoCoder1147 时空阵 题意&#x…

P4718-[模板]Pollard-Rho算法

正题 题目链接:https://www.luogu.com.cn/problem/P4718 题目大意 给出一个数nnn&#xff0c;如果它是质数则输出PrimePrimePrime&#xff0c;否则输出它的最大质因子。 解题思路 Pollard-Rho\text{Pollard-Rho}Pollard-Rho算法的前置知识是Miller-Rabin\text{Miller-Rabin}M…

T-Dongle-S3开发笔记——创建工程

创建Hello world工程 打开命令面板 方法1&#xff1a;查看->命令面板 方法2&#xff1a;按F1 选择ESP-IDF:展示示例项目 创建helloworld 选择串口 选择芯片 至此可以编译下载运行了 运行后打印的信息显示flash只有2M。但是板子上电flash是W25Q32 4MB的吗 16M-bit

hdu 1576 A/B

文章目录题目&#xff1a;题解&#xff1a;代码&#xff1a;hdu 1576题目&#xff1a; 要求(A/B)%9973&#xff0c;但由于A很大&#xff0c;我们只给出n(nA%9973)(我们给定的A必能被B整除&#xff0c;且gcd(B,9973) 1)。 Input 数据的第一行是一个T&#xff0c;表示有T组数据。…

ASP.NET Core 中断请求了解一下(翻译)

本文所讲方式仅适用于托管在Kestrel Server中的应用。如果托管在IIS和IIS Express上时&#xff0c;ASP.NET Core Module(ANCM)并不会告诉ASP.NET Core在客户端断开连接时中止请求。但可喜的是&#xff0c;ANCM预计在.NET Core 2.2中会完善这一机制。1. 引言假设有一个耗时的Act…

子数整数(luogu 1151)

子数整数 luogu 1151 题目大意 给出一个数k&#xff0c;让你在10000~30000中求出满足前三位&#xff0c;中间三位&#xff0c;后三位都可被k整除的数 输入样例 15输出样例 22555 25555 28555 30000数据范围 0<k<1000 解题思路 暴力枚举 代码 #include<cstd…

2021-10-22

扫描线&#xff1a; https://www.cnblogs.com/Parsnip/p/10887135.html https://blog.csdn.net/Emma2oo6/article/details/120584307 https://blog.csdn.net/weixin_30609331/article/details/96234492 LIS& LCS https://www.xuebuyuan.com/586419.html https://blog.csdn…

.net core实践系列之短信服务-架构优化

前言通过前面的几篇文章&#xff0c;讲解了一个短信服务的架构设计与实现。然而初始方案并非100%完美的&#xff0c;我们仍可以对该架构做一些优化与调整。同时我也希望通过这篇文章与大家分享一下&#xff0c;我的架构设计理念。源码地址&#xff1a;https://github.com/SkyCh…

Poj 1061 青蛙的约会

Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 143491 Accepted: 33018 Description 文章目录题目&#xff1a;思路:代码:题目传送题目&#xff1a; 两只青蛙在网上相识了&#xff0c;它们聊得很开心&#xff0c;于是觉得很有必要见一面。它们很高兴地发现它们住…