其实这两个题早就过了,本想着等看完KM算法一块发的,可是最近一直在忙考试和CF,迟迟没写出来,感觉再拖下去这两个就要忘了,所以还是先发出来吧。
POJ 3020 二分图的最小顶点覆盖 等于 最大匹配
定义:设K为G的一部分的点的集合,若图G中的每一条边至少有一个顶点在K中,则称K为G的一点顶点覆盖。
若G中不存在满足|K'| < |K|的点覆盖K' 则称K为G的最小点集覆盖。
设G为二分图,M为G的最大匹配,S为最小顶点覆盖的点集。
因为匹配M中的边两两没有交点,所以覆盖这M条边至少需要M个点。
若G中仍然存在两端都不为匹配点的边,则存在一条增广路,与M为最大匹配相矛盾。
对于一对匹配点,选择其中一个加入S时,若存在一条边未被覆盖,则将该点删除,加入另一点。
若两种情况均存在一天未被覆盖的边则说明崔在一条增广路,与M为最大匹配相矛盾。
故肯定存在一种选择方案使得在M对匹配点中选择M个点,使得所有的边均被覆盖,又因为覆盖M条
匹配边至少需要M个点,所以|s| == M。
POJ 3041 最大独立点集 等于 点的个数减去最大匹配
定义:设K为G的一部分的点的集合,若K中任意两点均不相邻,则称K为G的一个独立点集。
若G中不存在满足|K'| > |K|的独立点集K',则称K为G的最大独立点集。
在一个图中,独立集和顶点覆盖有互补的性质。
T是G的独立点集 <=> G中每条边至少有一个顶点在V(G) - T 中 <=> V(G) - T是G的一个顶点覆盖
而在二分图中最小点集覆盖等于最大二分匹配,所以最大独立点集等于点的个数减去最大匹配。
上述证明完全是自己瞎作的,不当之处,望不吝赐教。
下面说一下这两个题的思路:
其实两个题难度差不许多,基本属于拿来测代码的。
POJ 3041 可以将给出的点看成边,X,Y轴上的整数坐标位置看成点,则就抽象出了二分图,剩下的问题就迎刃而解了。
POJ 3020 给出的点作为点,两个相邻的点之间加一条边,因为不存在奇环,所以必为二分图。
POJ 3020
#include <iostream> #include <algorithm> #include <cstdlib> #include <cstdio> #include <cstring> #include <queue> #include <stack> #pragma comment(linker, "/STACK:1024000000"); #define LL unsigned long long int using namespace std; struct N { int v; N *next; }*head[410]; N *creat() { N *p = (N*)malloc(sizeof(N)); p->next = NULL; return p; }; int point[41][11]; char s[41][11]; int jx[] = {-1, 0, 1, 0}; int jy[] = { 0,-1, 0, 1}; int Matching_Point[410]; bool mark[410]; bool dfs(int s) { for(N *p = head[s]->next; p != NULL; p = p->next) { if(mark[p->v] == false) { mark[p->v] = true; if(Matching_Point[p->v] == -1 || dfs(Matching_Point[p->v])) { Matching_Point[p->v] = s; Matching_Point[s] = p->v; return true; } } } return false; } int Cal_Maximal_Matching(int n) { memset(Matching_Point,-1,sizeof(Matching_Point)); int i,ans = 0; for(i = 1; i < n; ++i) { if(Matching_Point[i] == -1) { memset(mark,false,sizeof(mark)); if(dfs(i)) { ans++; } } } return ans; } int main() { int T,n,m,i,j,k,top,x,y; N *p; for(i = 0; i < 401; ++i) { head[i] = creat(); } scanf("%d",&T); while(T--) { scanf("%d %d",&n,&m); for(i = 0; i <= n*m; ++i) { head[i]->next = NULL; } for(i = 0; i < n; ++i) { scanf("%*c%s",s[i]); } top = 1; memset(point,-1,sizeof(point)); for(i = 0; i < n; ++i) { for(j = 0; j < m; ++j) { if(s[i][j] == '*') { point[i][j] = top++; } } } for(i = 0; i < n; ++i) { for(j = 0; j < m; ++j) { if(point[i][j] != -1) { for(k = 0; k < 4; ++k) { x = i+jx[k]; y = j+jy[k]; if(0 <= x && x < n && 0 <= y && y < m && point[x][y] != -1) { p = creat(); p->v = point[x][y]; p->next = head[point[i][j]]->next; head[point[i][j]]->next = p; } } } } } printf("%d\n",top-1-Cal_Maximal_Matching(top)); } return 0 ; }
POJ 3041
#include <iostream> #include <algorithm> #include <cstdlib> #include <cstdio> #include <cstring> #include <queue> #include <stack> #pragma comment(linker, "/STACK:1024000000"); #define LL long long int using namespace std; const int MAXN = 1010; struct N { int v; N *next; }*head[MAXN]; N *creat() { N *p = (N *)malloc(sizeof(N)); p->next = NULL; return p; } bool mark[MAXN]; int color[MAXN]; int Match_Point[MAXN]; bool dfs(int t) { for(N *p = head[t]->next; p != NULL; p = p->next) { if(mark[p->v] == false) { mark[p->v] = true; if(Match_Point[p->v] == -1 || dfs(Match_Point[p->v])) { Match_Point[p->v] = t; Match_Point[t] = p->v; return true; } } } return false; } bool bfs(int n) { queue<int> q; int s,i; N *p; for(i = 1;i <= n; ++i) { if(color[i] == -1) { q.push(i); color[i] = 0; while(q.empty() == false) { s = q.front(); q.pop(); for(p = head[s]->next; p != NULL; p = p->next) { if(color[p->v] == -1) { color[p->v] = (color[s] == 1 ? 0 : 1); q.push(p->v); } else if(color[p->v] == color[s]) { return true; } } } } } return false; } int Cal_Maximal_Matching(int n) { int i,ans = 0; memset(Match_Point,-1,sizeof(Match_Point)); for(i = 1;i <= n; ++i) { if(Match_Point[i] == -1) { memset(mark,false,sizeof(mark)); if(dfs(i)) ans++; } } return ans; } int main() { int i,n,m,u,v; N *p; for(i = 0;i <= MAXN*2; ++i) { head[i] = creat(); } while(scanf("%d %d",&n,&m) != EOF) { for(i = 1;i <= n; ++i) { head[i]->next = NULL; } while(m--) { scanf("%d %d",&u,&v); v += n; p = creat(); p->v = v; p->next = head[u]->next; head[u]->next = p; p = creat(); p->v = u; p->next = head[v]->next; head[v]->next = p; } printf("%d\n",Cal_Maximal_Matching(n*2)); } return 0; }