一、题目:
当G为二分图时,试给出一个有效的算法以求解独立集问题。分析算法的运行时间,并证明算法的正确性
二、算法思路:
首先明确几个概念:
匹配E’: E’为边集E的子集,E’中各边均不相邻(或者说对任意点v∈V至多只有一条边与之关联)
非饱和点v: 匹配E’中没有边与点v关联
完美匹配E’: E’中没有非饱和点
给出引理:最大独立集 = 总点集 - 最大匹配边数
设V为总点集,V’为最大点独立集,M为最大匹配E’中的点集
即证 V’ = V - M/2
①若E’为完美匹配(有M=V)
则任意点v∈V,有且仅有一条边与之关联,此时对每一条边任取其一个顶点即可构成一个最大点独立集,即有 V’ = V - M/2
②若E’不为完美匹配
先证V’ >= V - M/2
(V-M)为非饱和点集,由于E’是最大匹配所以非饱和点集中的点不会互相连接(否则E’不是最大匹配),因此有 V’ >= V - M
考虑在 M 中的顶点 u:
1)若u与(V-M)中的点v’相连,则在最大匹配中与u相连的点v一定不与点v’相连(二分图性质),也一定不与M中的任何其它点相连(否则不为最大匹配),因此可以将点v加入独立集V’
2)若u不与(V-M)中的点相连,直接将u放入独立集V’中
总结而言,即匹配中每条边至少可以放一个邻接点到独立集中,所以有 V’ >= V - M + M/2 = V - M/2
后证V’ <= V - M/2
E’中每条边的两个邻接点至少有一个不能在V’中,所以至少有M/2个点不能在独立集中,因此V’ <= V - M/2
综上V’ = V - M/2
上述引理不仅可以得到最大独立集的点数,而且可以根据最大匹配直接得到这个最大独立集
三、程序设计框架:
(1)首先给出一个Ford-Fulkerson算法计算最大流问题:
构造一个名为edge的结构体包含该边的终点,容量(此处的容量会随着增广路径的出现而减小,这样就不用另外维护每条边流的值),反向边(便于残存网络的计算)三个属性,edge用于边的管理和流的计算
图 G=(V,E) 的存储方式使用邻接链表,维护 |V| 个edge数组,数组下表示该节点的序号,数组中的edge表示与该节点邻接的边。同时由于给出的图是二分图,另用V1、V2维护图中的两部分节点,最后设置一个起点连接V1所有顶点和一个终点连接V2所有顶点
(2)寻找增广路径时使用深度优先搜索(DFS):
数组used用于维护该点在搜索中是否被“发现”。由于二分图的特殊性,每条增广路径有且仅有4个顶点;由于每条边容量均为1,每条增广路径中间的两个点连成的边为最大匹配中的一条边。因此,在寻找增广路径的同时,只需要记录中间两个点中的任意一个即可(这些点组成上述的M/2集合),将这些点放入点集 independent_set_mirror
(3)找到独立集:
得到二分图的最大流f,根据上述推论可知该点集的独立集顶点数为|V|-f。遍历V中的顶点,不属于independent_set_mirror集合的点即为所寻找的独立集
(4)算法时间分析:
边的初始化:O(E)
寻找增广路径并得到最大流:取决于DFS的时间O(V+E)
遍历所有顶点得到独立集:O(V)
综上,时间为O(V+E)
#include<vector>
#include<algorithm>
#include<string.h>
#include<fstream>
#include<iostream>
#include<set>
#include<ctime>
using namespace std;
#define MAXVEX 10002
//用于表示边的结构体
struct edge
{
int to;//终点
int cap;//容量
int rev;//反向边
};
vector<edge> G[MAXVEX];//图的邻接表表示
set<int> independent_set_mirror;
bool used[MAXVEX];//DFS中用到的访问标记
int V,E;
//向图中增加一条从s到t容量为cap的边
void addEdge(int s, int t, int cap)
{
edge e;
e.cap = cap;e.to = t;e.rev = G[t].size();
G[s].push_back(e);
e.to = s; e.cap = 0; e.rev = G[s].size() - 1;
G[t].push_back(e);
}
//通过DFS寻找增广路径
int DFS(int v, int t, int f)
{
if (v == t)return f;
used[v] = true;
for (int i = 0; i < G[v].size(); ++i)
{
edge &e = G[v][i];
if (!used[e.to] && e.cap > 0)
{
int d = DFS(e.to, t, std::min(f, e.cap));
if (d > 0){
if(v==V)
independent_set_mirror.insert(e.to);
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
//求解从s到t的最大流
int max_flow(int s, int t)
{
int flow = 0;
for (;;)
{
memset(used, 0, sizeof(used));
int f = DFS(s, t, INT_MAX);
if (f == 0)return flow;
flow += f;
}
}
int main()
{
clock_t st,fn;
string fname("graph_100.txt");
string oname("outSet_100.txt");
ifstream in(fname.c_str());
ofstream out(oname.c_str());
if( !in.is_open() ) cout<<"fail to open "<<fname<<endl;
int a=1,b=1;
set<int> V1,V2;
in>>V>>E;
for(int j=0;j<E;j++)
{
if(!a&&!b)
break;
in>>a>>b;
addEdge(a,b,1);
if(V1.find(a)!=V1.end())
{
V2.insert(b);
}
else if(V2.find(a)!=V2.end())
{
V1.insert(b);
}
else if(V1.find(b)!=V1.end())
{
V2.insert(a);
}
else if(V2.find(b)!=V2.end())
{
V1.insert(a);
}
else{
V1.insert(a);
V2.insert(b);
}
}
for(set<int>::iterator it=V1.begin();it!=V1.end();it++)
{
addEdge(V,*it,1);
}
for(set<int>::iterator it=V2.begin();it!=V2.end();it++)
{
addEdge(*it,V+1,1);
}
st = clock();
int f = max_flow(V,V+1);
out<<"The size of independent set:"<<V-f<<endl;
// cout<<independent_set_mirror.size()<<endl;
for(int i=0;i<V;i++)
{
if(independent_set_mirror.find(i)==independent_set_mirror.end())
out<<i<<endl;
}
fn = clock();
cout<<"file name:"<<fname<<endl;
cout<<"running time:"<<(double)(fn-st)/CLOCKS_PER_SEC<<" sec"<<endl;
}