二分图知识点详解以及如何判断一个图是二分图,如何求二分图的最大匹配以及二分图的题目分析思考方向

这篇文章主要来讲述二分图的知识点以及相应的解决方法

首先我们先要知道什么是二分图

 

那这样的一个图具有什么样子的性质呢?

 

 

因此我们可以用染色法来判断一张无向图他是否是二分图

对于需要判断的图,我们可以先任意取无向图中的一个点,将其放进队列里面,同时将其染色,同时将其连接的边也放进去并且染成不同的颜色。进行多次操作。在我们操作这个队列的时候,我们会发现如果和某个点连接的点已经被染色并且颜色与这个点相同,那么我们可以判断这个图并不是一个二分图

 

我比较喜欢用BFS,那么就用bfs的方法来解决这个吧,接下来贴一下我的代码

void BFS(int sp)
{
	memset(vis,0,sizeof vis);
	pp.push(sp);
	vis[sp]=1;
	int flag=0;
	while(!pp.empty())
	{
		int x=pp.front();
		pp.pop();
		for(int i=0;i<q[x].size();i++)
		{
			int y=q[x][i];
			if(vis[y]==0)
			{
				vis[y]=-vis[x];
				pp.push(y);
			}
			else
			{
				if(vis[y]==vis[x])
				{
					flag=1;
				}
			}
		}
	}
	if(!flag) printf("NO\n");
	else printf("YES\n");
} 

(该方法用vector建边,无向边)

 

 

匹配

 

 

 

 

 

 

 

 

 

 那我们如何求得一个图的最大匹配呢?下面我们来介绍一下另外一个算法,叫做匈牙利算法

 

 

简单来说就是先确定集合中的一个点到另外一个集合的边,接下来如果有重复并且可以更改的时候就更改,从而找到最大的匹配

#include <bits/stdc++.h>
using namespace std;
int n,m;
const int maxn = 1e2 + 10;
int mp[maxn][maxn];
int vis[maxn];
int p[maxn];
int match(int x)
{
    for(int j=1;j<=m;j++)
    {
        if(mp[x][j]&&!vis[j])
        {
            vis[j]=1;
            if(p[j]==0||match(p[j]))
            {
                p[j]=x;
                return 1;
            }
        }
    }
    return 0;
}
int Hungarian()
{
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof vis);
        if(match(i)) cnt++;
    }
    return cnt;
}

我们平常如何来使用二部图解决问题呢?

问题1:求最小点覆盖

最小点覆盖=最大匹配

 

问题2:最小边覆盖(用多少条边可以覆盖所有的点)= 最大独立集= n-最大匹配

问题3:解决一些二维棋盘问题,比如hdu1281

D - 棋盘游戏

 HDU - 1281

小希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放尽量多的一些国际象棋里面的“车”,并且使得他们不能互相攻击,这当然很简单,但是Gardon限制了只有某些格子才可以放,小希还是很轻松的解决了这个问题(见下图)注意不能放车的地方不影响车的互相攻击。
所以现在Gardon想让小希来解决一个更难的问题,在保证尽量多的“车”的前提下,棋盘里有些格子是可以避开的,也就是说,不在这些格子上放车,也可以保证尽量多的“车”被放下。但是某些格子若不放子,就无法保证放尽量多的“车”,这样的格子被称做重要点。Gardon想让小希算出有多少个这样的重要点,你能解决这个问题么?

Input输入包含多组数据,
第一行有三个数N、M、K(1<N,M<=100 1<K<=N*M),表示了棋盘的高、宽,以及可以放“车”的格子数目。接下来的K行描述了所有格子的信息:每行两个数X和Y,表示了这个格子在棋盘中的位置。
Output对输入的每组数据,按照如下格式输出:
Board T have C important blanks for L chessmen.
Sample Input

3 3 4
1 2
1 3
2 1
2 2
3 3 4
1 2
1 3
2 1
3 2

Sample Output

Board 1 have 0 important blanks for 2 chessmen.
Board 2 have 3 important blanks for 3 chessmen.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 1e2 + 10;
int mp[maxn][maxn];
int vis[maxn];
int p[maxn];
int n,m,k;
int l,c;
typedef struct
{
    int xx,yy;
}node;
node a[maxn*maxn];
void Pre()
{
    memset(mp,0,sizeof mp);
    for(int i=1;i<=k;i++)
    {
        for(int j=i+1;j<=k;j++)
        {
            if(a[i].xx!=a[j].xx&&a[i].yy!=a[j].yy) mp[i][j]=1;
        }
    }
    for(int i=1;i<=k;i++)
    {
        for(int j=1;j<=k;j++)
        {
            printf("%d ",mp[i][j]);
        }
        printf("\n");
    } 
}
int match(int x)
{
    for(int i=1;i<=c;i++)
    {
        if(!vis[i]&&mp[x][i])
        {
            vis[i]=1;
            if(p[i]==0||match(p[i]))
            {
                p[i]=x;
                return 1;
            }
        }
    }
    return 0;
}
int hungary()
{
    memset(p,0,sizeof p);
    int cnt=0;
    for(int i=1;i<=l;i++)
    {
        memset(vis,0,sizeof vis);
        if(match(i)) cnt++;
    }
    return cnt;
}
int nnum=0;
int main()
{
    while(scanf("%d %d %d",&l,&c,&k)!=EOF)
    {
        nnum++;
        memset(mp,0,sizeof mp);
        for(int i=1;i<=k;i++)
        {
            int u,v;
            scanf("%d %d",&u,&v);
            a[i].xx=u;
            a[i].yy=v;
            mp[u][v]=1;
        }
        int ans2=hungary();
        //cout<<ans<<endl;
        int ans1=0;
        int ppre;
        for(int i=1;i<=k;i++)
        {
            mp[a[i].xx][a[i].yy]=0;
            ppre=hungary();
            if(ppre!=ans2) ans1++;
            mp[a[i].xx][a[i].yy]=1;
        }
    //    cout<<ans1<<" "<<ans2<<endl;
        printf("Board %d have %d important blanks for %d chessmen.\n",nnum,ans1,ans2);
    }
    return 0;
}

 那对于带权的二分图我们应该如何解决呢?

这里有一种神奇的方法叫做KM,但是由于目前还是没有理解,先放上OI-WIKI的解法吧

原网址:二分图最大权匹配 - OI Wiki (oi-wiki.org)

#include <bits/stdc++.h>
using namespace std;

template <typename T>
struct hungarian {  // km
  int n;
  vector<int> matchx;
  vector<int> matchy;
  vector<int> pre;
  vector<bool> visx;
  vector<bool> visy;
  vector<T> lx;
  vector<T> ly;
  vector<vector<T> > g;
  vector<T> slack;
  T inf;
  T res;
  queue<int> q;
  int org_n;
  int org_m;

  hungarian(int _n, int _m) {
    org_n = _n;
    org_m = _m;
    n = max(_n, _m);
    inf = numeric_limits<T>::max();
    res = 0;
    g = vector<vector<T> >(n, vector<T>(n));
    matchx = vector<int>(n, -1);
    matchy = vector<int>(n, -1);
    pre = vector<int>(n);
    visx = vector<bool>(n);
    visy = vector<bool>(n);
    lx = vector<T>(n, -inf);
    ly = vector<T>(n);
    slack = vector<T>(n);
  }

  void addEdge(int u, int v, int w) {
    g[u][v] = max(w, 0);  // 负值还不如不匹配 因此设为0不影响
  }

  bool check(int v) {
    visy[v] = true;
    if (matchy[v] != -1) {
      q.push(matchy[v]);
      visx[matchy[v]] = true;
      return false;
    }
    while (v != -1) {
      matchy[v] = pre[v];
      swap(v, matchx[pre[v]]);
    }
    return true;
  }

  void bfs(int i) {
    while (!q.empty()) {
      q.pop();
    }
    q.push(i);
    visx[i] = true;
    while (true) {
      while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int v = 0; v < n; v++) {
          if (!visy[v]) {
            T delta = lx[u] + ly[v] - g[u][v];
            if (slack[v] >= delta) {
              pre[v] = u;
              if (delta) {
                slack[v] = delta;
              } else if (check(v)) {
                return;
              }
            }
          }
        }
      }
      // 没有增广路 修改顶标
      T a = inf;
      for (int j = 0; j < n; j++) {
        if (!visy[j]) {
          a = min(a, slack[j]);
        }
      }
      for (int j = 0; j < n; j++) {
        if (visx[j]) {  // S
          lx[j] -= a;
        }
        if (visy[j]) {  // T
          ly[j] += a;
        } else {  // T'
          slack[j] -= a;
        }
      }
      for (int j = 0; j < n; j++) {
        if (!visy[j] && slack[j] == 0 && check(j)) {
          return;
        }
      }
    }
  }

  void solve() {
    // 初始顶标
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        lx[i] = max(lx[i], g[i][j]);
      }
    }

    for (int i = 0; i < n; i++) {
      fill(slack.begin(), slack.end(), inf);
      fill(visx.begin(), visx.end(), false);
      fill(visy.begin(), visy.end(), false);
      bfs(i);
    }

    // custom
    for (int i = 0; i < n; i++) {
      if (g[i][matchx[i]] > 0) {
        res += g[i][matchx[i]];
      } else {
        matchx[i] = -1;
      }
    }
    cout << res << "\n";
    for (int i = 0; i < org_n; i++) {
      cout << matchx[i] + 1 << " ";
    }
    cout << "\n";
  }
};

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  int n, m, e;
  cin >> n >> m >> e;

  hungarian<long long> solver(n, m);

  int u, v, w;
  for (int i = 0; i < e; i++) {
    cin >> u >> v >> w;
    u--, v--;
    solver.addEdge(u, v, w);
  }
  solver.solve();
}

 

posted @ 2021-07-22 18:01  Treasure-  阅读(176)  评论(0)    收藏  举报