最短增广路算法(SAP),采用广度优先的方法在残余网络中找去权值的最短增广路。从源点到汇点,像声音传播一样,总是找到最短的路径。在寻找路径时却多搜索了很多结点。
有人想到了一条妙计—贴标签。首先对所有的结点标记到汇点的最短距离,我们称之为高度。标高从汇点开始,用广度优先的方式,汇点的邻接点高度1,继续访问的结点高度是2,一直到源点结束。
贴好标签之后,从源点开始,沿着高度h(u)=h(v)+1且有可行邻接边(cap>flow)的方向前进,例如:h(1)=3,h(2)=2,h(4)= 1,h(6)=0。很快找到了汇点,然后沿着可增广路1—2—4—6增减流之后的残余网络。
再次从源点开始搜索,沿着高度h(u)= h(v)+1且有可行邻接边(cap>flow)的方向前进,h(1)=3,h(2)=2,走到这里无法走到4号结点,因为没有邻接边,3号结点不仅没有邻接边而且高度也不满足条件。也不能走到1号结点,因为h(1)=3。怎么办呢?
重贴标签:当前结点无法前进时,令当前结点的高度=所有邻接点高度的最小值+1;如果没有邻接边,则令当前结点的高度=结点数;退回一步;重新搜索。重贴标签后,h(2)= h(1)+1=4,退回一步到1号结点,重新搜索。
算法设计:
(1)确定合适数据结构。采用链式前向星存储混合网络。
(2)对网络结点贴标签,即标高操作。
(3)找可增广路。如果源点的高度≥结点数,算法结束;否则从源点开始,沿着高度h(u)=h(v)+1且有可行邻接边(cap>flow)的方向前进,如果到达汇点,则转向第4步;如果无法行进,则转向第5步。
(4)增流操作:沿着找到的可增广路同向边增流,反向边减流。
(5)重贴标签:如果拥有当前结点高度的结点只有一个,则转向第6步;令当前结点的高度=所有邻接点高度的最小值+1;如果没有可行邻接边,则令当前结点的高度=结点数;退回一步;转向第3步。
(6)算法结束,已经找到最大流。
在重贴标签之前判断当前高度为d[u]的结点个数是1,立即结束。
这是ISAP算法的重要优化,可以提前结束程序,很多时候提速非常明显(高达100倍以上)。
当前结点u无法行进时,说明u、t之间的连通性消失,但如果u是最后一个和t距离d[u]的点,说明此时s、t也不连通了。这是因为,虽然u、t已经不连通,但毕竟我们走的是最短路,其他点此时到t的距离一定大于d[u],因此其他点要到t,必然要经过一个和t距离为d[u]的点。
利用标签算法求解网络最大流。
(1)时间复杂度:从算法描述中可以看出,找到一条可增广路的时间是O(V),最多会执行O(VE)次,因为关键边的总数为O(VE),因此总的时间复杂度为O(V2E),其中V为结点个数,E为边的数量。
(2)空间复杂度:空间复杂度为O(V)。
hdu2732 ISAP
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1010;
const int M=1000100;
int cnt;
int head[N],pre[N],h[N],g[N];
struct Edge{
int v,next;
int cap,flow;
}E[M<<1];//双边
void init(){//初始化
memset(head,-1,sizeof(head));
cnt=0;
}
void add(int u,int v,int c){
E[cnt].v=v;
E[cnt].cap=c;
E[cnt].flow=0;
E[cnt].next=head[u];
head[u]=cnt++;
}
void adde(int u,int v,int c){
add(u,v,c);
add(v,u,0);
}
void set_h(int t,int n){//标高
queue<int> q;
memset(h,-1,sizeof(h));
memset(g,0,sizeof(g));
h[t]=0;
q.push(t);
while(!q.empty()){
int u=q.front();q.pop();
++g[h[u]];//高度为h[u]的节点个数
for(int i=head[u];~i;i=E[i].next){
int v=E[i].v;
if(h[v]==-1){
h[v]=h[u]+1;
q.push(v);
}
}
}
}
int ISAP(int s,int t,int n){
set_h(t,n);
int ans=0,u=s,d;
while(h[s]<n){
int i=head[u];
if(u==s)
d=inf;
for(;~i;i=E[i].next){
int v=E[i].v;
if(E[i].cap>E[i].flow&&h[u]==h[v]+1){
u=v;
pre[v]=i;
d=min(d,E[i].cap-E[i].flow);
if(u==t){
while(u!=s){
int j=pre[u];
E[j].flow+=d;
E[j^1].flow-=d;
u=E[j^1].v;
}
ans+=d;
//d=inf;
}
break;
}
}
if(i==-1){
if(--g[h[u]]==0)
break;
int hmin=n-1;
for(int j=head[u];~j;j=E[j].next)
if(E[j].cap>E[j].flow)
hmin=min(hmin,h[E[j].v]);
h[u]=hmin+1;
++g[h[u]];
if(u!=s)
u=E[pre[u]^1].v;
}
}
return ans;
}
int main(){
int T,cas=0;
string s;
scanf("%d",&T);
while(T--){
int n,m,d,src,dst;
int sum=0;//蜥蜴数
scanf("%d%d",&n,&d);
for(int i=0;i<n;i++){
cin>>s;
if(i==0){//第一次读入
m=s.size();
src=0,dst=2*n*m+1;
init();
}
for(int j=0;j<s.size();j++)
if(s[j]-'0'>0){ //大于0的拆点
int id=i*m+j+1; //当前点编号
adde(id,id+n*m,s[j]-'0');
if(i<d||i+d>=n||j<d||j+d>=m)//当前格子能直接跳出棋盘
adde(id+n*m,dst,inf);
else{ //不能直接跳出去
for(int k=0;k<n;k++)
for(int h=0;h<m;h++){
int id2=k*m+h+1;
if(id==id2) continue;
if(abs(i-k)+abs(j-h)<=d)
adde(id+n*m,id2,inf);
}
}
}
}
for(int i=0;i<n;i++){
cin>>s;
for(int j=0;j<s.size();j++){
int id=i*m+j+1;//当前点编号
if(s[j]=='L'){
++sum;
adde(src,id,1);
}
}
}
int ans=sum-ISAP(src,dst,dst+1);
if(ans==0) printf("Case #%d: no lizard was left behind.\n",++cas);
else if(ans==1) printf("Case #%d: 1 lizard was left behind.\n",++cas);
else printf("Case #%d: %d lizards were left behind.\n",++cas,ans);
}
return 0;
}
hdu280 Dinic
#include<cstdio>//G++ 9703ms
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=100010;
const int M=200010;
int cnt;
int head[N],d[N];
struct Edge{
int v,next;
int cap,flow;
}E[M<<1];//双边
void init(){//初始化
memset(head,-1,sizeof(head));
cnt=0;
}
void add(int u,int v,int c){
E[cnt].v=v;
E[cnt].cap=c;
E[cnt].flow=0;
E[cnt].next=head[u];
head[u]=cnt++;
}
void adde(int u,int v,int c){
add(u,v,c);
add(v,u,0);
}
bool bfs(int s,int t){//分层
memset(d,0,sizeof(d));
queue<int>q;
d[s]=1;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];~i;i=E[i].next){
int v=E[i].v;
if(!d[v]&&E[i].cap>E[i].flow){
d[v]=d[u]+1;
q.push(v);
if(v==t) return 1;
}
}
}
return 0;
}
int dfs(int u,int flow,int t){//在分层的基础上dfs
if(u==t) return flow;
int rest=flow;
for(int i=head[u];~i&&rest;i=E[i].next){
int v=E[i].v;
if(d[v]==d[u]+1&&E[i].cap>E[i].flow){
int k=dfs(v,min(rest,E[i].cap-E[i].flow),t);
if(!k) d[v]=0;
E[i].flow+=k;
E[i^1].flow-=k;
rest-=k;
}
}
return flow-rest;
}
int Dinic(int s,int t){
int maxflow=0;
while(bfs(s,t)){
maxflow+=dfs(s,inf,t);
}
return maxflow;
}
int main(){
int T,n,m,s,t;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int minx=inf;
int maxx=-inf;
for(int i=1;i<=n;i++){
int x,y;
scanf("%d%d",&x,&y);
if(x>maxx){
maxx=x;
t=i;
}
if(minx>x){
minx=x;
s=i;
}
}//找到源点和汇点。
init();
while(m--){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
adde(u,v,w);
adde(v,u,w);
}
printf("%d\n",Dinic(s,t));
}
return 0;
}
hdu4280 ISAP
#include<cstdio>//5366ms
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=100010;
const int M=200010;
int cnt;
int head[N],pre[N],h[N],g[N];
struct Edge{
int v,next;
int cap,flow;
}E[M<<1];//双边
void init(){//初始化
memset(head,-1,sizeof(head));
cnt=0;
}
void add(int u,int v,int c){
E[cnt].v=v;
E[cnt].cap=c;
E[cnt].flow=0;
E[cnt].next=head[u];
head[u]=cnt++;
}
void adde(int u,int v,int c){
add(u,v,c);
add(v,u,0);
}
void set_h(int t,int n){//标高
queue<int> q;
memset(h,-1,sizeof(h));
memset(g,0,sizeof(g));
h[t]=0;
q.push(t);
while(!q.empty()){
int u=q.front();q.pop();
++g[h[u]];//高度为h[u]的节点个数
for(int i=head[u];~i;i=E[i].next){
int v=E[i].v;
if(h[v]==-1){
h[v]=h[u]+1;
q.push(v);
}
}
}
}
int ISAP(int s,int t,int n){
set_h(t,n);
int ans=0,u=s,d;
while(h[s]<n){
int i=head[u];
if(u==s)
d=inf;
for(;~i;i=E[i].next){
int v=E[i].v;
if(E[i].cap>E[i].flow&&h[u]==h[v]+1){
u=v;
pre[v]=i;
d=min(d,E[i].cap-E[i].flow);
if(u==t){
while(u!=s){
int j=pre[u];
E[j].flow+=d;
E[j^1].flow-=d;
u=E[j^1].v;
}
ans+=d;
//d=inf;
}
break;
}
}
if(i==-1){
if(--g[h[u]]==0)
break;
int hmin=n-1;
for(int j=head[u];~j;j=E[j].next)
if(E[j].cap>E[j].flow)
hmin=min(hmin,h[E[j].v]);
h[u]=hmin+1;
++g[h[u]];
if(u!=s)
u=E[pre[u]^1].v;
}
}
return ans;
}
int main(){
int T,n,m,s,t;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int minx=inf;
int maxx=-inf;
for(int i=1;i<=n;i++){
int x,y;
scanf("%d%d",&x,&y);
if(x>maxx){
maxx=x;
t=i;
}
if(minx>x){
minx=x;
s=i;
}
}//找到源点和汇点。
init();
while(m--){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
adde(u,v,w);//双向!
adde(v,u,w);//双向!
}
printf("%d\n",ISAP(s,t,n));
}
return 0;
}
ISAP
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=100;
const int M=10000;
int cnt,d;
int head[N],pre[N],h[N],g[N];
struct Edge{
int v,next;
int cap,flow;
}E[M];
void init(){//初始化
memset(head,-1,sizeof(head));
cnt=0;
}
void add(int u,int v,int c){
E[cnt].v=v;
E[cnt].cap=c;
E[cnt].flow=0;
E[cnt].next=head[u];
head[u]=cnt++;
}
void set_h(int t,int n){//标高
queue<int> q;
memset(h,-1,sizeof(h));
memset(g,0,sizeof(g));
h[t]=0;
q.push(t);
while(!q.empty()){
int u=q.front();q.pop();
++g[h[u]];//高度为h[u]的节点个数
for(int i=head[u];~i;i=E[i].next){
int v=E[i].v;
if(h[v]==-1){
h[v]=h[u]+1;
q.push(v);
}
}
}
cout<<"初始化高度"<<endl;
cout<<"h[ ]=";
for(int i=1;i<=n;i++)
cout<<"\t"<<h[i];
cout<<endl;
}
int ISAP(int s,int t,int n){
set_h(t,n);
int ans=0,u=s,d;
while(h[s]<n){
int i=head[u];
if(u==s)
d=inf;
for(;~i;i=E[i].next){
int v=E[i].v;
if(E[i].cap>E[i].flow&&h[u]==h[v]+1){
u=v;
pre[v]=i;
d=min(d,E[i].cap-E[i].flow);
if(u==t){
cout<<endl;
cout<<"增广路径:"<<t;
while(u!=s){
int j=pre[u];
E[j].flow+=d;
E[j^1].flow-=d;
u=E[j^1].v;
cout<<"--"<<u;
}
cout<<"增流:"<<d<<endl;
ans+=d;
d=inf;
}
break;
}
}
if(i==-1){
if(--g[h[u]]==0)
break;
int hmin=n-1;
for(int j=head[u];~j;j=E[j].next)
if(E[j].cap>E[j].flow)
hmin=min(hmin,h[E[j].v]);
h[u]=hmin+1;
cout<<"重贴标签后高度"<<endl;
cout<<"h[ ]=";
for(int i=1;i<=n;i++)
cout<<" "<<h[i];
cout<<endl;
++g[h[u]];
if(u!=s)
u=E[pre[u]^1].v;
}
}
return ans;
}
void printg(int n){//输出网络
cout<<endl;
cout<<"----------网络(链式前向星):----------"<<endl;
for(int i=1;i<=n;i++){
cout<<"v"<<i<<" ["<<head[i];
for(int j=head[i];~j;j=E[j].next)
cout<<"]--["<<E[j].v<<"\t"<<E[j].cap<<"\t"<<E[j].flow<<"\t"<<E[j].next;
cout<<"]"<<endl;
}
cout<<endl;
}
void printflow(int n){//输出实流边
cout<<endl;
cout<<"----------实流边:----------"<<endl;
for(int i=1;i<=n;i++)
for(int j=head[i];~j;j=E[j].next)
if(E[j].flow>0){
cout<<"v"<<i<<"--"<<"v"<<E[j].v<<"\t"<<E[j].flow;
cout<<endl;
}
}
/*
请输入结点个数n和边数m:
6 9
1 3 10
1 2 12
2 4 8
3 5 13
3 2 2
4 6 18
4 3 5
5 6 4
5 4 6
*/
int main(){
int n,m;
int u,v,w;
cout<<"请输入结点个数n和边数m:"<<endl;
cin>>n>>m;
init();
cout<<"请输入两个结点u,v及边(u--v)的容量w:"<<endl;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
add(u,v,w);
add(v,u,0);
}
cout<<endl;
printg(n);//输出初始网络
cout<<"网络的最大流值:"<<ISAP(1,n,n)<<endl;
cout<<endl;
printg(n);//输出最终网络
printflow(n);//输出实流边
return 0;
}
POJ3281 ISAP
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=410;
const int M=20500;
int cnt;
int head[N],pre[N],h[N],g[N];
struct Edge{
int v,next;
int cap,flow;
}E[M<<1];//双边
void init(){//初始化
memset(head,-1,sizeof(head));
cnt=0;
}
void add(int u,int v,int c){
E[cnt].v=v;
E[cnt].cap=c;
E[cnt].flow=0;
E[cnt].next=head[u];
head[u]=cnt++;
}
void adde(int u,int v,int c){
add(u,v,c);
add(v,u,0);
}
void set_h(int t,int n){//标高
queue<int> q;
memset(h,-1,sizeof(h));
memset(g,0,sizeof(g));
h[t]=0;
q.push(t);
while(!q.empty()){
int u=q.front();q.pop();
++g[h[u]];//高度为h[u]的节点个数
for(int i=head[u];~i;i=E[i].next){
int v=E[i].v;
if(h[v]==-1){
h[v]=h[u]+1;
q.push(v);
}
}
}
}
int ISAP(int s,int t,int n){
set_h(t,n);
int ans=0,u=s,d;
while(h[s]<n){
int i=head[u];
if(u==s)
d=inf;
for(;~i;i=E[i].next){
int v=E[i].v;
if(E[i].cap>E[i].flow&&h[u]==h[v]+1){
u=v;
pre[v]=i;
d=min(d,E[i].cap-E[i].flow);
if(u==t){
while(u!=s){
int j=pre[u];
E[j].flow+=d;
E[j^1].flow-=d;
u=E[j^1].v;
}
ans+=d;
//d=inf;
}
break;
}
}
if(i==-1){
if(--g[h[u]]==0)
break;
int hmin=n-1;
for(int j=head[u];~j;j=E[j].next)
if(E[j].cap>E[j].flow)
hmin=min(hmin,h[E[j].v]);
h[u]=hmin+1;
++g[h[u]];
if(u!=s)
u=E[pre[u]^1].v;
}
}
return ans;
}
int main(){
int n,f,d,k,x,y;
scanf("%d%d%d",&n,&f,&d);
int s=0,t=f+2*n+d+1;
init();
for(int i=1;i<=f;i++)//源点到食物连边
adde(s,i,1);
for(int i=1;i<=n;i++)//牛到牛拆点连边
adde(f+i,f+n+i,1);
for(int i=1;i<=d;i++)//饮料到汇点连边
adde(f+2*n+i,t,1);
for(int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
for(int j=1;j<=x;j++){//食物到牛入点连边
scanf("%d",&k);
adde(k,f+i,1);
}
for(int j=1;j<=y;j++){//牛出点到饮料连边
scanf("%d",&k);
adde(f+n+i,f+2*n+k,1);
}
}
printf("%d\n",ISAP(s,t,t+1));
return 0;
}
POJ3436 ISAP
#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=110;
const int M=10100;
int n,m;
int cnt;
int head[N],pre[N],h[N],g[N];
int num[N];
int input[N][20],output[N][20];
int vis[N][N];
struct Edge{
int v,next;
int cap,flow;
}E[M<<1];//双边
void init(){//初始化
memset(head,-1,sizeof(head));
memset(vis,0,sizeof vis);
cnt=0;
}
void add(int u,int v,int c){
E[cnt].v=v;
E[cnt].cap=c;
E[cnt].flow=0;
E[cnt].next=head[u];
head[u]=cnt++;
}
void adde(int u,int v,int c){
add(u,v,c);
add(v,u,0);
}
void set_h(int t,int n){//标高
queue<int> q;
memset(h,-1,sizeof(h));
memset(g,0,sizeof(g));
h[t]=0;
q.push(t);
while(!q.empty()){
int u=q.front();q.pop();
++g[h[u]];//高度为h[u]的节点个数
for(int i=head[u];~i;i=E[i].next){
int v=E[i].v;
if(h[v]==-1){
h[v]=h[u]+1;
q.push(v);
}
}
}
}
int ISAP(int s,int t,int n){
set_h(t,n);
int ans=0,u=s,d;
while(h[s]<n){
int i=head[u];
if(u==s)
d=inf;
for(;~i;i=E[i].next){
int v=E[i].v;
if(E[i].cap>E[i].flow&&h[u]==h[v]+1){
u=v;
pre[v]=i;
d=min(d,E[i].cap-E[i].flow);
if(u==t){
while(u!=s){
int j=pre[u];
E[j].flow+=d;
E[j^1].flow-=d;
u=E[j^1].v;
}
ans+=d;
//d=inf;
}
break;
}
}
if(i==-1){
if(--g[h[u]]==0)
break;
int hmin=n-1;
for(int j=head[u];~j;j=E[j].next)
if(E[j].cap>E[j].flow)
hmin=min(hmin,h[E[j].v]);
h[u]=hmin+1;
++g[h[u]];
if(u!=s)
u=E[pre[u]^1].v;
}
}
return ans;
}
bool check(int x,int y){
return x==y||y==2;
}
bool ck1(int d){ //没有1
for(int i=1;i<=n;i++)
if(input[d][i]==1) return false;
return true;
}
bool ck2(int d){ //全1
for(int i=1;i<=n;i++)
if(output[d][i]!=1) return false;
return true;
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=m;i++){
scanf("%d",&num[i]);
for(int j=1;j<=n;j++)
scanf("%d",&input[i][j]);
for(int j=1;j<=n;j++)
scanf("%d",&output[i][j]);
}
init();
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++){
if(i==j) continue;
bool flag=true;
for(int k=1;k<=n;k++)
if(!check(output[i][k],input[j][k])){
flag=false;
break;
}
if(flag){
adde(i+m,j,num[i]);
//cout<<i+m<<"\t"<<j<<"\t"<<num[i]<<endl;
vis[i+m][j]=1;
}
}
for(int i=1;i<=m;i++){
adde(i,i+m,num[i]);//拆点
if(ck1(i)) adde(0,i,num[i]);
if(ck2(i)) adde(i+m,2*m+1,num[i]);
}
printf("%d",ISAP(0,2*m+1,2*m+2));
int cnt=0;
for(int i=1;i<=m;i++)
for(int j=head[i+m];~j;j=E[j].next)
if(vis[i+m][E[j].v]&&E[j].flow>0) cnt++;//连接数
printf(" %d\n",cnt);
for(int i=1;i<=m;i++)
for(int j=head[i+m];~j;j=E[j].next)
if(vis[i+m][E[j].v]&&E[j].flow>0)
printf("%d %d %d\n",i,E[j].v,E[j].flow);//机器A B flow
}
return 0;
}