您的位置:首页 > 其它

二分图最大匹配,最小点覆盖,最小路径覆盖,二分图最大独立集

2016-11-10 11:46 423 查看
1) )数 一个二分图中的最大匹配数等于这个图中的最小点覆盖数

König 定理是一个二分图中很重要的定理,它的意思是,一个二分图中的最大匹配数等于这个图中的最小

点覆盖数。如果你还不知道什么是最小点覆盖,我也在这里说一下:假如选了一个点就相当于覆盖了以它

为端点的所有边,你需要选择最少的点来覆盖所有的边

UVA11419最小点覆盖,打印匹配位置。

#include<bitset>
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
#define F first
#define S second
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

inline int in()
{
int res=0;char c;int f=1;
while((c=getchar())<'0' || c>'9')if(c=='-')f=-1;
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
const double eps = 1e-8;
const double PI = acos(-1.0);
const int maxn = 1000 + 5;
struct BPM{
int n,m; //左右顶点个数
vector<int>G[maxn]; //邻接表
int left[maxn] ;//left[i] 右边第i个点的匹配点编号,-1不存在
bool T[maxn]; //右边第i个点是否已标记

int right[maxn] ;
bool S[maxn];
void init(int _n,int _m){
n = _n; m = _m;
for(int i=0;i<n;i++)G[i].clear();
}
void addEdge(int u,int v){
G[u].push_back(v);
}
bool match(int u){
S[u] = true;
for(int i=0;i<G[u].size();i++){
int v = G[u][i];
if(!T[v]){
T[v] = true;
if(left[v] == -1 || match(left[v])){
left[v] = u;
right[u] = v;
return true;
}
}
}
return false;
}

//求最大匹配
int solve(){
mem(left,-1); mem(right,-1);
int ans =0;
for(int u=0;u<n;u++){
mem(S,0); mem(T,0);
if(match(u)) ans++;
}
return ans;
}
// 求最小覆盖。X和Y为最小覆盖中的点集
int mincover(vector<int>& X, vector<int>& Y){
int ans = solve();
mem(S,0);mem(T,0);
for(int u=0;u<n;u++)
if(right[u] == -1) match(u);// 从所有X未盖点出发增广
for(int u=0;u<n;u++)
if(!S[u]) X.push_back(u);// X中的未标记点
for(int u=0;u<m;u++)
if(T[u]) Y.push_back(u); // Y中的已标记点
return ans;
}
}solver;
int R,C,N;

int main(){
while(scanf("%d%d%d",&R,&C,&N) == 3&&R &&C &&N){
solver.init(R,C);
for(int i=0;i<N;i++){
int r,c;scanf("%d%d",&r,&c); r--;c--;
solver.addEdge(r,c);
}
vector<int>X,Y;
int ans = solver.mincover(X,Y);
printf("%d",ans);
for(int i=0;i<X.size();i++) printf(" r%d",X[i]+1);
for(int i=0;i<Y.size();i++) printf(" c%d",Y[i]+1);
puts("");
}
return 0;
}


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

独立集:图中任意两个顶点都不相连的顶点集合
独立集和点覆盖正好是互补的。

LA3415最大独立集。

给出一些学生,有一些冲突关系,问最多可以选出多少学生,使得两两之间没有冲突。

分成男女两个集合建立,冲突的建边,形成了二分图。

然后用学生总数 - 二分图最大匹配数就得到了最大独立集数。

#include<bitset>
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
#define F first
#define S second
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

inline int in()
{
int res=0;char c;int f=1;
while((c=getchar())<'0' || c>'9')if(c=='-')f=-1;
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
const int maxn = 500 + 5;
//邻接矩阵写法
struct BPM{
int n,m;
int G[maxn][maxn];
int left[maxn];
bool T[maxn];
void init(int _n,int _m){
n = _n; m = _m;
mem(G,0);
}
bool match(int u){
for(int v=0;v<m;v++)
if(G[u][v] && !T[v]){
T[v] = true;
if(left[v] == -1 || match(left[v])){
left[v] = u;
return true;
}
}
return false;
}
//求最大匹配
int solve(){
mem(left,-1);
int ans = 0;
for(int u=0;u<n;u++){
mem(T,0);
if(match(u)) ans++;
}
return ans;
}

}solver;
struct Student{
int h;
string music,sport;
Student(int _h,string _music,string _sport){
h = _h; music = _music; sport = _sport;
}

};
bool conflict(const Student&a,const Student&b){
return abs(a.h - b.h)<=40&& a.music == b.music &&
a.sport != b.sport;
//有一个不满足,就是有冲突,可以建边;
}
int main(){
int T;
cin>>T;
while(T--){
int n;cin>>n;
vector<Student> male,female;
for(int i=0;i<n;i++){
int h;
string gender,music,sport;
cin>>h>>gender>>music>>sport;
if(gender[0] == 'M')male.push_back(Student(h,music,sport));
else female.push_back(Student(h,music,sport));

}
int x = male.size();
int y = female.size();
solver.init(x,y);
//只去建立有冲突的边,将集合分成男女构造出了二分图。
//男女之间可能有冲突的情况,进行建边
for(int i=0;i<x;i++)
for(int j=0;j<y;j++)
if(conflict(male[i],female[j])) solver.G[i][j] = 1;
printf("%d\n",x+y-solver.solve());

}
return 0;
}



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

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

有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,那么恰好可以经

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

由上面可以得出:

1.一个单独的顶点是一条路径;

2.如果存在一路径 p1,p2,......pk,其中 p1 为起点,pk 为终点,那么在覆盖图中,顶点 p1,p2,......pk 不再

与其它的

顶点之间存在有向边.

最小路径覆盖就是找出最小的路径条数,使之成为 G 的一个路径覆盖.

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

LA3126

#include<bitset>
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
#define F first
#define S second
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;

inline int in()
{
int res=0;char c;int f=1;
while((c=getchar())<'0' || c>'9')if(c=='-')f=-1;
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
const int maxn = 500 + 5;
//邻接矩阵写法
struct BPM{
int n,m;
int G[maxn][maxn];
int left[maxn];
bool T[maxn];
void init(int _n,int _m){
n = _n; m = _m;
mem(G,0);
}
bool match(int u){
for(int v=0;v<m;v++)
if(G[u][v] && !T[v]){
T[v] = true;
if(left[v] == -1 || match(left[v])){
left[v] = u;
return true;
}
}
return false;
}
//求最大匹配
int solve(){
mem(left,-1);
int ans = 0;
for(int u=0;u<n;u++){
mem(T,0);
if(match(u)) ans++;
}
return ans;
}

}solver;
int X1[maxn],Y1[maxn],X2[maxn],Y2[maxn],t1[maxn],t2[maxn];
inline int dist(int a,int b,int c,int d){
return abs(a-c) + abs(b-d);
}
int main(){
int T;scanf("%d",&T);
while(T--){
int n;scanf("%d",&n);
for(int i=0;i<n;i++){
int h,m;
scanf("%d:%d%d%d%d%d",&h,&m,&X1[i],&Y1[i],&X2[i],&Y2[i]);
t1[i] = h*60+m;
t2[i] = t1[i] + dist(X1[i],Y1[i],X2[i],Y2[i]);
}
solver.init(n,n);
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(t2[i] + dist(X2[i],Y2[i],X1[j],Y1[j]) < t1[j])
solver.G[i][j] = 1;
printf("%d\n",n - solver.solve());
}
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  二分图最大匹配
相关文章推荐