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


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());
    }
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值