图的应用--二部图的判定及利用匈牙利算法求二部图的最大匹配

1. 图类

#ifndef GRAPH_H
#define GRAPH_H

const int maxvertex=100; //最多可以有100个顶点
const int maxedge=1000;  //最多可以有1000条边
const double maxweight=1e10; //权值最大值

#include <iostream>
#include <cstdlib>
#include <memory>

using namespace std;

template<class ElemType>
class Graph {
	private:
		bool IsDirect; //是否是有向图,false表示无向图
		bool IsWeight; //是否带权图
	protected:
		typedef struct lnode { //邻接链表结点
			int sequence; //顶点的序号0,1,2,...
			struct lnode *next;
		};
		typedef struct tnode { //表结点
			ElemType vertex; //顶点的值
			lnode *link;
		};
		typedef struct Graph_struct {
			tnode *array;
			double **connect; //邻接矩阵,矩阵元素是边上的权值
			int edgenum;  //图的边数
			int vertexnum; //图的顶点数
		};
		Graph_struct graph;
		bool *visited; //记录顶点是否被访问过
		void DFSTranverse(int start);
		void Get(int index,ElemType& e); //根据索引号获取顶点 
	public:
		Graph(bool IsDirect,bool IsWeight);	
		void DFS();
		void show();
		virtual ~Graph();
};
#endif


以上定义既包括了图的邻接链表也包括了图的邻接矩阵,既适合有向图也适合无向图,既适合有权图,也适合无权图。

 

2. 图类实现

#include "Graph.h"

template<class ElemType>

Graph<ElemType>::Graph(bool IsDirect,bool IsWeight) {
	this->IsDirect=IsDirect;
	this->IsWeight=IsWeight;

	int n;
	int i,j;

	cout<<"请输入图的顶点数:";
	cin>>n;

	if(!cin.good()) {
		cerr<<"输入参数格式不合法!"<<endl;
		return;
	}

	if(n<=0 || n>maxvertex) {
		cerr<<"顶点数应该大于0且小于"<<maxvertex<<endl;
		return;
	}
	graph.vertexnum=n;

	graph.connect=new double*[graph.vertexnum];
	for(i=0;i<graph.vertexnum;i++) {
		graph.connect[i]=new double[graph.vertexnum]; 
	}

	for(i=0;i<graph.vertexnum;i++) {
		for(j=0;j<graph.vertexnum;j++) graph.connect[i][j]=0;
	}

	this->visited=new bool[graph.vertexnum];
	
	graph.array=new tnode[graph.vertexnum];
	if(!graph.array) {
		cerr<<"内存分配失败!"<<endl;
		return;
	}

	cout<<"请输入顶点信息"<<endl;
	ElemType vertex;
	for(i=0;i<graph.vertexnum;i++) {
		cout<<"请输入第"<<i<<"个顶点:";
		cin>>vertex;
		if(!cin.good()) {
		cerr<<"输入顶点格式不合法!"<<endl;
		i--;
		continue;
		}
		else {
			graph.array[i].vertex=vertex;
			graph.array[i].link=0;
		}
	}

	cout<<"请输入边信息"<<endl;
	int m=0;
	int from; //边的起点
	int to;  //边的终点
	double weight;

	cout<<"如果输入边的起点或终点是-1时,则程序停止接受输入!"<<endl;
	
	int k=0; //边的个数
	lnode *p;
	while(true) {
		cout<<"边的起点:";
		cin>>from;
		if(!cin.good()) {
			cerr<<"输入起点不合法!"<<endl;
			break;;
		}
		if(from==-1) break;
		else if(from<0 || from>=graph.vertexnum) {
			cerr<<"输入范围应该大于等于0且小于"<<graph.vertexnum<<endl;
			break;;
		}

		cout<<"边的终点:";
		cin>>to;
		if(!cin.good()) {
			cerr<<"输入终点不合法!"<<endl;
			break;
		}	
		if(to==-1) break;
		else if(to<0 || to>=graph.vertexnum) {
			cerr<<"输入范围应该大于等于0且小于"<<graph.vertexnum<<endl;
			return;
		}

		if(IsWeight==true) {
			cout<<"输入边的权值:";
			cin>>weight;
			if(!cin.good()) {
				cout<<"输入权值不合法!"<<endl;
				break;
			}
			if(weight>=maxweight || weight<0) {
				cerr<<"权值应该大于等于0且小于"<<maxweight<<endl;
				break;
			}
		}
		p=new lnode;
		p->next=0;
		p->sequence=to;
		if(IsWeight) graph.connect[from][to]=weight;
		else graph.connect[from][to]=1;

		p->next=graph.array[from].link; //插入邻接链表
		graph.array[from].link=p;

		if(!IsDirect) { //是无向图
			lnode *q=new lnode;
			q->next=0;
			q->sequence=from;
			if(IsWeight) graph.connect[to][from]=weight;
			else graph.connect[to][from]=1;

			q->next=graph.array[to].link;
			graph.array[to].link=q;
		}
		k++;
	}
	graph.edgenum=k;
}

template<class ElemType>
void Graph<ElemType>::DFS() {
	int i;
	for(i=0;i<graph.vertexnum;i++) {
		this->visited[i]=false;
	}
	for(i=0;i<graph.vertexnum;i++) {
		if(visited[i]==false) DFSTranverse(i);
	}
}

template<class ElemType>
void Graph<ElemType>::DFSTranverse(int start) {
	visited[start]=true;
	cout<<"第"<<start<<"个顶点是:"<<graph.array[start].vertex<<endl;
	int j;
	lnode *p=graph.array[start].link;
	while(p) {
		j=p->sequence;
		if(!this->visited[j]) DFSTranverse(j);
		p=p->next;
	}
}

template<class ElemType>
void Graph<ElemType>::Get(int index,ElemType &e) {
	if(index<0 || index>=graph.vertexnum) {
		cerr<<"索引号应该大于等于0且小于"<<graph.vertexnum<<endl;
		exit(1);
	}
	e=graph.array[index].vertex;
}

template<class ElemType>
void Graph<ElemType>::show() {
	int m,n;
	double weight;

	n=graph.vertexnum;
	m=graph.edgenum;

	cout<<"图有"<<n<<"个顶点,"<<m<<"条边"<<endl;
	cout<<"图的顶点信息:"<<endl;
	this->DFS();

	int i,j;
	int from,to;
	int k=1;

	ElemType e;

	cout<<"图的边信息:"<<endl;
	for(i=0;i<n;i++) {
		for(j=0;j<n;j++) {
			if(graph.connect[i][j]) {	
				from=i;
				to=j;
				cout<<endl;
				cout<<"第"<<k++<<"条边"<<endl;
				cout<<"起点:";
				e=graph.array[from].vertex;
				cout<<e<<" ";
				cout<<"终点:";
				e=graph.array[to].vertex;
				cout<<e<<" ";
				if(IsWeight)
				{
					weight=graph.connect[i][j];
					cout<<"权值:"<<weight<<endl;
				}
			}
		}
	}
}

template<class ElemType>
Graph<ElemType>::~Graph() {
	delete []visited;
	visited=0;
	int i;
	lnode *p,*q;
	for(i=0;i<graph.vertexnum;i++) {
		p=graph.array[i].link;
		while(p) {
			q=p;
			p=p->next;
			graph.array[i].link=p;
			delete q;
		}
	}
	delete []graph.array;
	graph.array=0;
	for(i=0;i<graph.vertexnum;i++) {
		delete []graph.connect[i];
		graph.connect[i]=0;
	}
	delete graph.connect;
	graph.connect=0;
}


 

3. 测试图类

	Graph<char>g1(0,0); //不带权值的无向图
	g1.show();


4.

 

	
	Graph<char>g2(0,1); //带权值的无向图
	g2.show();

	
	Graph<char>g3(1,0); //不带权值的有向图
	g3.show();

	
	Graph<char>g4(0,0); //带权值的有向图
	g4.show();


4. 二部图的判定及匈牙利算法

 

#include "graph.h" #include "graph.cpp" #include <string> #include <vector> #include <map>

using namespace std;

enum Color{red,blue,black};  //定义颜色

template<class ElemType> class Bipartite_Graph:public Graph<ElemType> {  private:   bool flag;  //表示此图是不是二分图   vector<int>Red;   vector<int>Blue;   Color *vertexcolor;   map<int,int>match; //<蓝点,与蓝点匹配的红点>   int count;  //匹配数  public:   Bipartite_Graph(bool IsDirect,bool IsWeight):Graph<ElemType>(IsDirect,IsWeight) {    vertexcolor=new Color[graph.vertexnum];    flag=true;    count=0;    int i,j;    for(i=0;i<graph.vertexnum;i++) vertexcolor[i]=black;    for(i=0;i<graph.vertexnum;i++) visited[i]=false;    for(i=0;i<graph.vertexnum;i++) {     if(visited[i]==false) {      IsBipartite_Graph_DFS(i,red);  //判断输入的图是不是二部图,如果是,则将图的顶点划分为红点集和蓝点集      if(flag==false) return;     }    }    count=0;    vector<int>::iterator it;    for(it=Blue.begin();it!=Blue.end();it++) {     pair<int,int>p(*it,-1); // <蓝点,-1>     match.insert(p);    }   }   void Hungary() {  //匈牙利算法    vector<int>::iterator it;    for(it=Red.begin();it!=Red.end();it++) {     for(int i=0;i<graph.vertexnum;i++) visited[i]=false;     if(DFS(*it)) count++;    }        ElemType red,blue;    cout<<"最大匹配是:"<<endl;    map<int,int>::iterator pos;    for(pos=match.begin();pos!=match.end();pos++) {     if(pos->first==-1 || pos->second==-1) continue;  //此时match中有顶点没有匹配     Get(pos->first,blue);      Get(pos->second,red);      cout<<"红点:"<<red<<"到"<<"蓝点"<<blue<<endl;    }    cout<<"共有"<<count<<"个匹配"<<endl;   }   void show() {    Graph<ElemType>::show();    vector<int>::iterator it;    cout<<endl<<"红点集是:"<<endl;    for(it=Red.begin();it!=Red.end();it++) {     cout<<*it<<" ";    }    cout<<endl;    cout<<endl<<"蓝点集是:"<<endl;    for(it=Blue.begin();it!=Blue.end();it++) {     cout<<*it<<" ";    }    cout<<endl;   }   ~Bipartite_Graph() {    delete []vertexcolor;    int i;   }  private:   void IsBipartite_Graph_DFS(int start,Color color) {  //pre是顶点start在DFS序列中的直接前驱顶点    int j;    visited[start]=true;    cout<<start<<" ";    if(color==red) cout<<"red"<<endl;    else cout<<"blue"<<endl;    if(vertexcolor[start]==black) {  //此顶点没有打上颜色     vertexcolor[start]=color;     if(color==red) {      Red.push_back(start);  //将此顶点加入红点集      color=blue;  //将颜色转化     }     else {      Blue.push_back(start);      color=red;     }    }

   lnode *p=graph.array[start].link;    while(p) {     j=p->sequence;     if(!visited[j]) {      if(flag==true) IsBipartite_Graph_DFS(j,color); //去检查此顶点的相邻顶点     }     else {      if(vertexcolor[j]==vertexcolor[start]) {  //相关联的两个顶点颜色一样,则此图不是二分图       cerr<<"不是二分图!"<<endl;       Red.clear();       Blue.clear();       flag=false;       return;      }     }     p=p->next;    }   }   bool DFS(int k) {    vector<int>::iterator it;    for(it=Blue.begin();it!=Blue.end();it++)  //试探每一个蓝点集的顶点    {     if(graph.connect[k][*it] && !visited[*it])  //如果此蓝点与红点相连,且该蓝点没有被访问过     {      visited[*it]=true;      if(match[*it]==-1 || DFS(match[*it]))  //如果该蓝点没有与其匹配的红点,或者蓝点              //有匹配的红点,那么从该红点查找匹配      {       match[*it]=k;    //找到增广路径,将其取反       return true;      }     }    }    return false;   } };

void main() {  Bipartite_Graph<string>bg(0,0);  map<int,int>::iterator  it;  bg.show();  bg.Hungary(); }

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值