求解二分图的独立集问题

一、题目:
当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;
}














  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值