圆桌问题 网络流Isap算法
Posted wanluN1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了圆桌问题 网络流Isap算法相关的知识,希望对你有一定的参考价值。
圆桌问题 最大网络流Isap算法
图片来源《趣学算法》 人民邮电出版社 陈小玉
算法设计
图片来源《趣学算法》 人民邮电出版社 陈小玉
代码实现
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int INF=0x3fffffff;
const int N=100;
const int M=10000;
int top;
int h[N],pre[N],g[N];
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 m,int n);//输出结果
int main(int argc,char**argv){
init();
int n=0,m=0,sum=0,total=0;//sum为总人数
int cost=0;
cout<<"输入代表团数量m与会议桌数量n:";
cin>>m>>n;
total=m+n;
cout<<"输入每个代表团的人数\\n";
for(int i=1;i<=m;i++){
cin>>cost;
sum+=cost;
add(0,i,cost);//源点到代表团的边,容量为代表团人数量
}
cout<<"输入每个会议桌可安排的人数\\n";
for(int j=m+1;j<=total;j++){
cin>>cost;
add(j,total+1,cost);//会议桌到汇点的边的容量为会议桌可安排的数量
}
//架设代表团到会议桌的边
for(int i=1;i<=m;i++){
for(int j=m+1;j<=total;j++){
add(i,j,1);//容量为1,因为每个桌子上只能有一个代表团的人
}
}
printGraph(total+2);
if(sum==Isap(0,total+1,total+2)){//不能有人落单没有分配到桌子
cout<<"会议桌安排成功\\n";
printResult(m,n);
}else{
cout<<"无法合理分配\\n";
}
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++;
}
//匹配算法
/*
@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;
}
void printResult(int m,int n){
cout<<"方案:\\n";
for(int i=1;i<=m;i++){
//遍历其邻接边
for(int j=V[i].first;~j;j=E[j].next){
if(E[j].flow==1){
cout<<i<<"--"<<E[j].v-m<<" \\n";
}
}
}
}
//输出邻接表
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");
}
}
/*Test*/
/*
输入代表团数量m与会议桌数量n:4 5
输入每个代表团的人数
2 4 3 5
输入每个会议桌可安排的人数
3 4 2 5 4
网络邻接表:
[0]---flow:0 cap:5---[4]---flow:0 cap:3---[3]---flow:0 cap:4---[2]---flow:0 cap:2---[1]
[1]---flow:0 cap:1---[9]---flow:0 cap:1---[8]---flow:0 cap:1---[7]---flow:0 cap:1---[6]---flow:0 cap:1---[5]---flow:0 cap:0---[0]
[2]---flow:0 cap:1---[9]---flow:0 cap:1---[8]---flow:0 cap:1---[7]---flow:0 cap:1---[6]---flow:0 cap:1---[5]---flow:0 cap:0---[0]
[3]---flow:0 cap:1---[9]---flow:0 cap:1---[8]---flow:0 cap:1---[7]---flow:0 cap:1---[6]---flow:0 cap:1---[5]---flow:0 cap:0---[0]
[4]---flow:0 cap:1---[9]---flow:0 cap:1---[8]---flow:0 cap:1---[7]---flow:0 cap:1---[6]---flow:0 cap:1---[5]---flow:0 cap:0---[0]
[5]---flow:0 cap:0---[4]---flow:0 cap:0---[3]---flow:0 cap:0---[2]---flow:0 cap:0---[1]---flow:0 cap:3---[10]
[6]---flow:0 cap:0---[4]---flow:0 cap:0---[3]---flow:0 cap:0---[2]---flow:0 cap:0---[1]---flow:0 cap:4---[10]
[7]---flow:0 cap:0---[4]---flow:0 cap:0---[3]---flow:0 cap:0---[2]---flow:0 cap:0---[1]---flow:0 cap:2---[10]
[8]---flow:0 cap:0---[4]---flow:0 cap:0---[3]---flow:0 cap:0---[2]---flow:0 cap:0---[1]---flow:0 cap:5---[10]
[9]---flow:0 cap:0---[4]---flow:0 cap:0---[3]---flow:0 cap:0---[2]---flow:0 cap:0---[1]---flow:0 cap:4---[10]
[10]---flow:0 cap:0---[9]---flow:0 cap:0---[8]---flow:0 cap:0---[7]---flow:0 cap:0---[6]---flow:0 cap:0---[5]
[11]
初始化标高h={ 0-3 1-2 2-2 3-2 4-2 5-1 6-1 7-1 8-1 9-1 10-0 }
增广路径:10---9---4---0增流: 1
增广路径:10---8---4---0增流: 1
增广路径:10---7---4---0增流: 1
增广路径:10---6---4---0增流: 1
增广路径:10---5---4---0增流: 1
增广路径:10---9---3---0增流: 1
增广路径:10---8---3---0增流: 1
增广路径:10---7---3---0增流: 1
增广路径:10---9---2---0增流: 1
增广路径:10---8---2---0增流: 1
重贴标签后的高度
h[ ]= 2 2 2 2 1 1 3 1 1 0 -1
增广路径:10---6---2---0增流: 1
增广路径:10---5---2---0增流: 1
增广路径:10---9---1---0增流: 1
增广路径:10---8---1---0增流: 1
重贴标签后的高度
h[ ]= 2 2 2 2 1 1 3 1 1 0 -1
会议桌安排成功
方案:
1--5
1--4
2--5
2--4
2--2
2--1
3--5
3--4
3--3
4--5
4--4
4--3
4--2
4--1
*/
以上是关于圆桌问题 网络流Isap算法的主要内容,如果未能解决你的问题,请参考以下文章