暂时只包括与最大匹配相关的问题。
求最大独立集,最小路径覆盖等等大多数题目都可以转化为求最大匹配用匈牙利算法解决。
1.最大匹配(边集)
此类问题最直接,直接用匈牙利算法即可。
HDU 2063 过山车
http://acm.hdu.edu.cn/showproblem.php?pid=2063
二分图最大匹配模版题。
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1654
易想到求最大独立集但是时间复杂度太高,可以变成二分图最大匹配来做,重在建模。
2.最小路径覆盖(边集)
对于不存在孤立的点的图,|最小路径覆盖|+|最大匹配|=V(顶点数)
因此可以通过求最大匹配来做。
这个等式在DAG图中也成立,详见http://www.cnblogs.com/jackiesteed/articles/2043934.html。对于DAG图可以拆点来做。
HDU 1151 Air Raid
http://acm.hdu.edu.cn/showproblem.php?pid=1151
1 #include<iostream>
2 #include<cstring>
3 #include<cmath>
4 #include<algorithm>
5 #include<cstdlib>
6 #include<cstdio>
7 #include<vector>
8 #define MAXN 1005
9 using namespace std;
10 bool gl[MAXN][MAXN];
11 int link[MAXN];
12 int n;
13 bool vis[MAXN];
14
15 bool match(int x)
16 {
17 for(int i=1; i<=n; ++i)
18 if(gl[x][i]&&!vis[i])
19 {
20 vis[i]=true;
21 if(link[i]==-1||match(link[i]))
22 {
23 link[i]=x;
24 return true;
25 }
26 }
27 return false;
28 }
29 int main()
30 {
31 int T;
32 scanf("%d",&T);
33 while(T--)
34 { int m;
35 scanf("%d%d",&n,&m);
36 memset(gl,0,sizeof(gl));
37 for(int i=0;i<m;++i)
38 {
39 int x,y;
40 scanf("%d%d",&x,&y);
41 gl[x][y]=true;
42 }
43 int res=0;
44 memset(link,-1,sizeof(link));
45 for(int i=1; i<=n; ++i)
46 {
47 memset(vis,0,sizeof(vis));
48 if(match(i)) res++;
49 }
50 printf("%d\n",n-res);
51 }
52 return 0;
53 }
求DAG的最小路径覆盖。
3.最小顶点覆盖(点集)
在二分图中|最大匹配|=|最小顶点覆盖|,但是注意要解决的问题本身并不是最大匹配,理解了问题本身才能建模。
HDU 1054 Strategic Game
http://acm.hdu.edu.cn/showproblem.php?pid=1054
此题可以用树形DP来做也可以用求最小顶点覆盖,但是时间较慢。拆点,改成双向图,用邻接表。
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<vector>
#define MAXN 1505
using namespace std;
vector<int> gl[MAXN];
int link[MAXN];
int n;
bool vis[MAXN];
void init()
{
memset(link,-1,sizeof(link));
memset(vis,0,sizeof(vis));
}bool match(int x)
{
for(int i=0; i<gl[x].size(); ++i)
{
int &u=gl[x][i];
if(!vis[u])
{
vis[u]=true;
if(link[u]==-1||match(link[u]))
{
link[u]=x;
return true;
}
}
}return false;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(gl,0,sizeof(gl));
for(int i=0; i<n; ++i) gl[i].clear();
for(int i=0; i<n; ++i)
{
int now,t,cn;
scanf("%d:",&now);
scanf("(%d)",&cn);
while(cn--)
{
scanf("%d",&t);
gl[now].push_back(t);
gl[t].push_back(now);
}
}
init();
int res=0;
for(int i=0; i<n; ++i)
{
memset(vis,0,sizeof(vis));
if(match(i)) res++;
}
printf("%d\n",res/2);
}
return 0;
}
HDU 1150 Machine Schedule
http://acm.hdu.edu.cn/showproblem.php?pid=1150
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<vector>
#define MAXN 1005
using namespace std;
bool gl[MAXN][MAXN];
int link[MAXN];
int n,m;
bool vis[MAXN];bool match(int x)
{
for(int i=1; i<=m; ++i)
if(gl[x][i]&&!vis[i])
{
vis[i]=true;
if(link[i]==-1||match(link[i]))
{
link[i]=x;
return true;
}
}
return false;
}
int main()
{
int t;
while(scanf("%d",&n)&&n)
{
scanf("%d%d",&m,&t);
memset(gl,0,sizeof(gl));
for(int i=0; i<t; ++i)
{
int x,y,z;
scanf("%d%d%d",&z,&x,&y);
gl[x][y]=true;
}
int res=0;
memset(link,-1,sizeof(link));
for(int i=1; i<=n; ++i)
{
memset(vis,0,sizeof(vis));
if(match(i)) res++;
}
printf("%d\n",res);
}
return 0;
}
4.最大独立集(点集)
|最大独立集|+|最小顶点覆盖|=V
求最大独立集是NP问题,但是在二分图中|最大匹配|=|最小顶点覆盖|,所以这个问题也有了高效的算法。
HDU 1045 Fire Net
http://acm.hdu.edu.cn/showproblem.php?pid=1045
这个可以暴力求最大独立集。
HDU 2768 Cat vs. Dog
http://acm.hdu.edu.cn/showproblem.php?pid=2768
根据矛盾之间建图,求最大独立集。
HDU 1068 Girls and Boys
http://acm.hdu.edu.cn/showproblem.php?pid=1068
题意很明显就是求二分图最大独立集。但是由于没说给的是男还是女,所以需要拆点,得到的最大匹配数要除以2,再套用公式得最大独立集。
1 #include<iostream>
2 #include<cstring>
3 #include<cmath>
4 #include<algorithm>
5 #include<cstdlib>
6 #include<cstdio>
7 #include<vector>
8 #define MAXN 1005
9 using namespace std;
10 bool gl[MAXN][MAXN];
11 int link[MAXN];
12 int n;
13 bool vis[MAXN];
14 void init()
15 {
16 memset(link,-1,sizeof(link));
17 memset(vis,0,sizeof(vis));
18 }
19
20 bool match(int x)
21 {
22 for(int i=0; i<n; ++i)
23 if(gl[x][i]&&!vis[i])
24 {
25 vis[i]=true;
26 if(link[i]==-1||match(link[i]))
27 {
28 link[i]=x;
29 return true;
30 }
31 }
32 return false;
33 }
34 int main()
35 {
36 while(scanf("%d",&n)!=EOF)
37 {
38 memset(gl,0,sizeof(gl));
39 for(int i=0; i<n; ++i)
40 {
41 int now,t,cn;
42 scanf("%d: ",&now);
43 scanf("(%d) ",&cn);
44 while(cn--)
45 {
46 scanf("%d",&t);
47 gl[now][t]=true;
48 }
49 }
50 init();
51 int res=0;
52 for(int i=0; i<n; ++i)
53 {
54 memset(vis,0,sizeof(vis));
55 if(match(i)) res++;
56 }
57 printf("%d\n",(n*2-res)/2);
58 }
59 return 0;
60 }
5.判断二分图
此类问题可以BFS或DFS解决,注意图可能不连通。
HDU 4751 Divide Groups
http://acm.hdu.edu.cn/showproblem.php?pid=4751
根据不互相认识这一关系连边,判断二分图。
ACM -二分图题目小结(更新中)