配对方案Isap算法--线性规划网络流
Posted 11biscuits
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了配对方案Isap算法--线性规划网络流相关的知识,希望对你有一定的参考价值。
简略介绍
二分图:又称作二部图,设G=(V,E)是一个无向图,
如果结点集V可分割为两个互不相交的子集(V1,V2),
并且图中的每条边(i,j)所关联的两个结点i和j分别属于这两个不同的结点集(i<-V1,j<-V2)
匹配:在图论中,一个匹配是一个边的集合,其中任意两条边都没有公共结点。
最大匹配:一个图所有匹配中,边数最多的匹配,称为这个图的最大匹配。
V1 V2 边 1–6
1 5 1–7
2–5
2 6 3–7
4–5
3
7
4
配对网络,增加源点s->v1[i],与汇点v2[j]->t
*/
/*配对网络算法
(1) 构建网络:根据输入的数据,增加源点汇点,每条边的容量设为1、创建混合网络
(2) 求网络最大流
(3) 输出最大流值就是最大匹配对数
(4) 搜索V1结点的邻接表,流量为1的边对应的邻接点就是一个最佳配对方案
C++代码实现
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int INF=0x3fffff;
const int N=100;
const int M=10000;
int top;//V1 V2结点集总共的结点数量
int h[N],pre[N],graph[N];
int g[N];//存放 g[x] 高度为x的节点的总个数
//邻接表相关数据结构
struct Vertex{
int first;
}V[N];
struct Edge{
int v,next;
int cap,flow;
}E[M];
void init();//初始化
void add(int u,int v,int c);
void add_edge(int u,int v,int c);
void printGraph(int n);//输出邻接表
int Isap(int s,int t,int n);//匹配算法
void set_h(int t,int n);
void printResult(int n);//输出配对方案
int main(void){
int n,m,total;
int u,v;
cout<<"请输入女员工人数m和男员工人数n: \\n";
cin>>m>>n;
//初始化
init();
total=m+n;//V1 V2总共的结点数量
//初始化网络
for(int i=1;i<=m;i++){
//源点到V1结点集中的点 0为源点
add(0,i,1);
}
//将右边一层连接到汇点
for(int i=m+1;i<=total;i++){
add(i,total+1,1);
}
cout<<"输入可以配和的V1与V2结点可组合的对,u--v,当u=-1且v=-1时结束输入\\n";
while(cin>>u>>v,u+v!=-2){
add(u,v,1);//u->v cap=1 v->u flow=0
}
//输出邻接表
printGraph(total+2);
cout<<"最大匹配对数: "<<Isap(0,total+1,total+2)<<endl;
//输出邻接表
printGraph(total+2);
printResult(m);
return 0;
}
//初始化
void init(){
memset(V,-1,sizeof(V));//初始化每个结点的first为-1,初始化邻接表
top=0;//边列表递增序号,存储边的列表使用多少了
}
//添加网络边
void add(int u,int v,int c){
add_edge(u,v,c);//cap=c
add_edge(v,u,0);//flow=0
}
void add_edge(int u,int v,int c){
E[top].v=v;//邻接表插入新节点,头插法
E[top].cap=c;
E[top].flow=0;
E[top].next=V[u].first;
V[u].first=top++;
}
//输出邻接表
void printGraph(int n){
cout<<"网络邻接表:\\n";
for(int i=0;i<=n;i++){
printf("[%d]",i);
for(int j=V[i].first;~j;j=E[j].next){//-1 的反码为 0
printf("---flow:%d cap:%d---[%d]",E[j].flow,E[j].cap,E[j].v);
}
printf("\\n");
}
}
//匹配算法
/*
@Param s: 源点下标
@Param t: 汇点下标
@Param n: 总结点数
*/
int Isap(int s,int t,int n){
set_h(t,n);//进行标高
int ans=0,u=s;//ans最大流量,u当前探索到的结点
int d;
while(h[s]<n){//源点高度小于总结点个数时
int i=V[u].first;
if(u==s){//当前在源点时
d=INF;
}
//搜索当前结点的邻接边
for(;i!=-1;i=E[i].next){
int v=E[i].v;//u-->v
//判断是否满足探索条件:有可增量 且 h[u]-1=h[v]
if(E[i].cap>E[i].flow&&h[u]-1==h[v]){//有可增量,且层级-1
u=v;//满足条件则当前位置移到v E[i]
pre[v]=i;//设置v结点的前驱为i 即记录边u------->v
//迭代最小增量
d=min(d,E[i].cap-E[i].flow);
if(u==t){//探索到了汇点
printf("增广路径:%d",t);
while(u!=s){
int j=pre[u];//即增广路汇点的前驱边E[j]
E[j].flow+=d;//E[j]流量增d
E[j^1].flow-=d;//j的反向边流量-d
/*
^1:创建边时是成对创建的0^1=1 1^1=0 2^1=3 3^1=2 9^1=8 5^1=4
*/
u=E[j^1].v;
cout<<"---"<<u;
}
printf("增流: %d\\n",d);//增广路径增流为路上的最小增量
ans+=d;
d=INF;
}
break;//找到一条可行邻接边,即找到了下一个要去的点,退出for循环,停止寻找可行邻接边
}
}
if(-1==i){//所有邻接边搜索完毕,无法前行
if(--g[h[u]]==0){//该高度结点只有1个,算法结束
break;
}
int hmin=n-1;
for(int j=V[u].first;j!=-1;j=E[j].next){//搜索u的邻接边
if(E[j].cap>E[j].flow){//有可增量
hmin=min(hmin,h[E[j].v]);
}
}
h[u]=hmin+1;//可行邻接边的中的最小高度+1,否则将走不通的点的高度为节点个数
printf("重贴标签后的高度\\n");
printf("h[ ]=");
for(int i=1;i<=n;i++){
printf(" %d",h[i]);
}
printf("\\n");
++g[h[u]];//重新贴标签后该高度的结点数+1
if(u!=s){//当前结点不是源点
u=E[pre[u]^1].v;//退回一步
}
}
}
return ans;
}
//广度优先标高函数
void set_h(int t,int n){
queue<int>Q;//广度优先搜索
memset(h,-1,sizeof(h));//初始化各结点的高为-1
memset(g,0,sizeof(g));//全部高度的结点数量为0
h[t]=0;//汇点高度为0
Q.push(t);//汇点入队列
while(!Q.empty()){
int v=Q.front();Q.pop();//对头出队列
++g[h[v]];//高度为h[v]的数量+1
for(int i=V[v].first;i!=-1;i=E[i].next){//遍历结点v的临界点及v-->some
int u=E[i].v;
if(h[u]==-1){//还没有标记过
h[u]=h[v]+1;
Q.push(u);//入队列
}
}
}
cout<<"初始化标高";
cout<<"h={";
for(int i=0;i<n;i++){
cout<<" "<<i<<"-"<<h[i];
}
cout<<" }"<<endl;
}
//输出配对结果
/*
@Param n:女人个数,左层节点数
*/
void printResult(int n){
cout<<"配对方案:\\n";
for(int i=1;i<=n;i++){
//遍历其邻接边
for(int j=V[i].first;~j;j=E[j].next){
if(E[j].flow>0){
cout<<i<<"--"<<E[j].v<<" ";
break;
}
}
}
cout<<"\\n";
}
测试样例
Test Simple
5 7
1 6
1 8
2 7
2 8
2 11
3 7
3 9
3 10
4 12
4 9
5 10
-1 -1
Console Output
请输入女员工人数m和男员工人数n:
5 7
输入可以配和的V1与V2结点可组合的对,u–v,当u=-1且v=-1时结束输入
1 6
1 8
2 7
2 8
2 11
3 7
3 9
3 10
4 12
4 9
5 10
-1 -1
网络邻接表:
[0]—flow:0 cap:1—[5]—flow:0 cap:1—[4]—flow:0 cap:1—[3]—flow:0 cap:1—[2]—flow:0 cap:1—[1]
[1]—flow:0 cap:1—[8]—flow:0 cap:1—[6]—flow:0 cap:0—[0]
[2]—flow:0 cap:1—[11]—flow:0 cap:1—[8]—flow:0 cap:1—[7]—flow:0 cap:0—[0]
[3]—flow:0 cap:1—[10]—flow:0 cap:1—[9]—flow:0 cap:1—[7]—flow:0 cap:0—[0]
[4]—flow:0 cap:1—[9]—flow:0 cap:1—[12]—flow:0 cap:0—[0]
[5]—flow:0 cap:1—[10]—flow:0 cap:0—[0]
[6]—flow:0 cap:0—[1]—flow:0 cap:1—[13]
[7]—flow:0 cap:0—[3]—flow:0 cap:0—[2]—flow:0 cap:1—[13]
[8]—flow:0 cap:0—[2]—flow:0 cap:0—[1]—flow:0 cap:1—[13]
[9]—flow:0 cap:0—[4]—flow:0 cap:0—[3]—flow:0 cap:1—[13]
[10]—flow:0 cap:0—[5]—flow:0 cap:0—[3]—flow:0 cap:1—[13]
[11]—flow:0 cap:0—[2]—flow:0 cap:1—[13]
[12]—flow:0 cap:0—[4]—flow:0 cap:1—[13]
[13]—flow:0 cap:0—[12]—flow:0 cap:0—[11]—flow:0 cap:0—[10]—flow:0 cap:0—[9]—flow:0 cap:0—[8]—flow:0 cap:0—[7]—flow:0 cap:0—[6]
[14]
初始化标高h={ 0-3 1-2 2-2 3-2 4-2 5-2 6-1 7-1 8-1 9-1 10-1 11-1 12-1 13-0 }
增广路径:13—10—5---0增流: 1
增广路径:13—9---4—0增流: 1
重贴标签后的高度
h[ ]= 2 2 2 2 2 1 1 1 1 3 1 1 0 -1
重贴标签后的高度
h[ ]= 2 2 2 2 2 1 1 1 3 3 1 1 0 -1
增广路径:13—7---3—0增流: 1
增广路径:13—11—2---0增流: 1
增广路径:13—8---1—0增流: 1
重贴标签后的高度
h[ ]= 2 2 2 2 2 1 1 1 3 3 1 1 0 -1
最大匹配对数: 5
网络邻接表:
[0]—flow:1 cap:1—[5]—flow:1 cap:1—[4]—flow:1 cap:1—[3]—flow:1 cap:1—[2]—flow:1 cap:1—[1]
[1]—flow:1 cap:1—[8]—flow:0 cap:1—[6]—flow:-1 cap:0—[0]
[2]—flow:1 cap:1—[11]—flow:0 cap:1—[8]—flow:0 cap:1—[7]—flow:-1 cap:0—[0]
[3]—flow:0 cap:1—[10]—flow:0 cap:1—[9]—flow:1 cap:1—[7]—flow:-1 cap:0—[0]
[4]—flow:1 cap:1—[9]—flow:0 cap:1—[12]—flow:-1 cap:0—[0]
[5]—flow:1 cap:1—[10]—flow:-1 cap:0—[0]
[6]—flow:0 cap:0—[1]—flow:0 cap:1—[13]
[7]—flow:-1 cap:0—[3]—flow:0 cap:0—[2]—flow:1 cap:1—[13]
[8]—flow:0 cap:0—[2]—flow:-1 cap:0—[1]—flow:1 cap:1—[13]
[9]—flow:-1 cap:0—[4]—flow:0 cap:0—[3]—flow:1 cap:1—[13]
[10]—flow:-1 cap:0—[5]—flow:0 cap:0—[3]—flow:1 cap:1—[13]
[11]—flow:-1 cap:0—[2]—flow:1 cap:1—[13]
[12]—flow:0 cap:0—[4]—flow:0 cap:1—[13]
[13]—flow:0 cap:0—[12]—flow:-1 cap:0—[11]—flow:-1 cap:0—[10]—flow:-1 cap:0—[9]—flow:-1 cap:0—[8]—flow:-1 cap:0—[7]—flow:0 cap:0—[6]
[14]
配对方案:
1–8 2–11 3–7 4–9 5–10
以上是关于配对方案Isap算法--线性规划网络流的主要内容,如果未能解决你的问题,请参考以下文章