为什么二分图的最大二分匹配数等于最小点覆盖数

md,昨晚上的tc500pt竟然能没做出来,说到底还是没有对算法有深入的理解。。。。2333333,不扯了。。

我想说的是这里有一篇很详细的文章,不过感觉有点过于详细了。http://www.matrix67.com/blog/archives/116

按照博文中所说,做完一次匈牙利算法后我们从右边的未选点出发,开始找增广路(显然已经找不到了),这个过程中会搜出一颗颗的交错树,起点都是未匹配点,终点都是匹配点,然后有如下结论:

左边被搜到的点以及右边未访问的点就是我们要找的最小点覆盖的点集

以下证明两个东西

1:这些点的数量恰好为M(最大匹配数)

2:这些点能覆盖所有的边

3:这些点是最少的满足条件的点(证明了1,这个就自然证明了,因为匹配边就有M条了,覆盖这些边就至少需要M个点)

证明1:所选点集中的每个点都对应了一条匹配边,而且没有两个点属于同一条匹配边

证明2:所有的边都逃不出所选点集的“魔爪”,我们把边分一下类:匹配边,非匹配边

非匹配边可分为两种

(1:左边是匹配点,右边是非匹配点,这种边的左端点已经是我们挑选的点集中的点了,所以这种边肯定已经被控制了。

(2:左边是非匹配点,右边是匹配点,这种边的右端点显然不可能到达,因为假如从右边某个未访问点到达这个右端点,然后再到左边的非匹配点,交错树的首尾就都是非匹配边了,那么就找到增广路了,矛盾,所以这种边的右端点都是未访问点,会被选入覆盖点集,所以这种边也被控制了

来个例题:

给你一个50*50的网格,网格中有三种东东"。" O” “X”

你可以在空地也就是“。”放X,如果一个O的周围的空地都被X了,这个O就会变成“。”,你要采取合适的策略使得你能得到最多的“。”

显然我们有一个美好的愿望,所有的O都变成“。”,然后所有的“。”都保持不变,假设这样的总数是n+m

但是显然很多时候都做不到,于是我们需要去掉最少的不能满足条件的O,把最多的O与“。” 的总量留下来

其实就是最大点独立集问题了,当一个O独立的时候,他的周围没有任何一个“。”,也就天然满足了题目了要求。

srm 594  level2

#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <ctime>
using namespace std;

class FoxAndGo3
{
public: 
    int maxEmptyCells(vector <string> board);
    
    
// BEGIN CUT HERE
	public:
	void run_test(int Case) { if ((Case == -1) || (Case == 0)) test_case_0(); if ((Case == -1) || (Case == 1)) test_case_1(); if ((Case == -1) || (Case == 2)) test_case_2(); if ((Case == -1) || (Case == 3)) test_case_3(); if ((Case == -1) || (Case == 4)) test_case_4(); if ((Case == -1) || (Case == 5)) test_case_5(); }
	private:
	template <typename T> string print_array(const vector<T> &V) { ostringstream os; os << "{ "; for (typename vector<T>::const_iterator iter = V.begin(); iter != V.end(); ++iter) os << '\"' << *iter << "\","; os << " }"; return os.str(); }
	void verify_case(int Case, const int &Expected, const int &Received) { cerr << "Test Case #" << Case << "..."; if (Expected == Received) cerr << "PASSED" << endl; else { cerr << "FAILED" << endl; cerr << "\tExpected: \"" << Expected << '\"' << endl; cerr << "\tReceived: \"" << Received << '\"' << endl; } }
	void test_case_0() { string Arr0[] = {"o.o",
 ".o.",
 "o.o"}; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 5; verify_case(0, Arg1, maxEmptyCells(Arg0)); }
	void test_case_1() { string Arr0[] = {"...",
 ".o.",
 "..."}
; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 8; verify_case(1, Arg1, maxEmptyCells(Arg0)); }
	void test_case_2() { string Arr0[] = {"xxxxx",
 "xxoxx",
 "xo.ox",
 "xxoxx",
 "xxxxx"}
; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 4; verify_case(2, Arg1, maxEmptyCells(Arg0)); }
	void test_case_3() { string Arr0[] = {".xox.",
 ".o.ox",
 "x.o.o",
 "ox.ox",
 ".ox.."}
 ; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 12; verify_case(3, Arg1, maxEmptyCells(Arg0)); }
	void test_case_4() { string Arr0[] = {"o.o.o",
 ".ox..",
 "oxxxo",
 "..x..",
 "o.o.o"}
; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 12; verify_case(4, Arg1, maxEmptyCells(Arg0)); }
	void test_case_5() { string Arr0[] = {"...",
 "...",
 "..."}; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arg1 = 9; verify_case(5, Arg1, maxEmptyCells(Arg0)); }

// END CUT HERE

};

// BEGIN CUT HERE
int main()
{
    FoxAndGo3 ___test;
    ___test.run_test(-1);
    return 0;
}
// END CUT HERE
int n1,n2;
vector<int> g[55*55];
int id[55][55];
bool vis[55*55];
int match[55*55];
bool dfs(int u)
{
    for(int i = 0; i < g[u].size(); i++) {
        int v = g[u][i];
        if(vis[v]) continue;
        vis[v] = true;
        if(match[v] == -1 || dfs(match[v])) {
            match[v] = u;
            return true;
        }
    }
    return false;
}
int dx[]={
    0,0,1,-1
};
int dy[]={
    1,-1,0,0
};
int FoxAndGo3::maxEmptyCells(vector <string> board)
{
  int n = board.size(),m = board[0].length();
  n1 = 0; n2 = 0;
  for(int i = 0; i < board.size(); i++) {
      for(int j = 0; j < board[i].length(); j++) {
          if(board[i][j] == '.') {
              n1++;
              id[i][j] = n1;
          }
      }
  }
  for(int i = 1; i <= n1; i++) g[i].clear();
  for(int i = 0; i < board.size(); i++) {
      for(int j = 0; j < board[i].length(); j++) {
          if(board[i][j] == 'o') {
              n2++;
              for(int k = 0; k < 4; k++) {
                  int x = i + dx[k];
                  int y = j + dy[k];
                  if(x >= 0 && x < n && y >=0 && y < m && board[x][y]=='.') {
                      g[id[x][y]].push_back(n2);
                  }
              }
          } 
      }
  }
  int ret = n1 + n2;
  for(int i = 1; i <= n2; i++) match[i]=-1;
  for(int i = 1; i <= n1; i++) {
      for(int j = 1; j <= n2; j++) vis[j]=false;
      if(dfs(i)) ret--;
  }
  return ret;
}


你可能感兴趣的:(为什么二分图的最大二分匹配数等于最小点覆盖数)