##网络流##
来源:互联网 发布:telnet 发送字符 端口 编辑:程序博客网 时间:2024/06/11 05:09
HLPP参考:http://blog.sina.com.cn/s/blog_60707c0f01010oqt.html
ISAP参考:http://blog.sina.com.cn/s/blog_6165e6920100ea08.html
参考:http://blog.sina.com.cn/s/blog_476a25110100mag7.html
http://wenku.baidu.com/link?url=Q3y_0x9BaO1L1W5Kr3AJzgCUVJVKNjG8UGw2nt44d6HDWTT3QLWygZ8IT_lb-V6GNjYoV4Fh74bsB88uTMeJA4xWBnbAyNfUr9VRCo-rCFS
//algo1://Ford-Fulkerson//O(n*m*U)#define N 115struct arc{ int c,f;}e[N][N];int flag[N],al[N],pre[N];int n,m,np,nc;int ford(int s,int t,int n){ while (1) { memset(flag,255,sizeof(flag)); memset(pre,255,sizeof(pre)); memset(al,255,sizeof(al)); queue<int > q; flag[s]=0; pre[s]=-1; al[s]=inf; q.push(s); while (!q.empty()&&flag[t]==-1) { int v=q.front(); q.pop(); for (int i=0;i<n;i++) if (flag[i]==-1) { if (e[v][i].f<e[v][i].c) { flag[i]=0; pre[i]=v; al[i]=min(al[v],e[v][i].c-e[v][i].f); q.push(i); } else if (e[i][v].f>0) { flag[i]=0; pre[i]=-v; al[i]=min(al[v],e[i][v].f); q.push(i); } } flag[v]=1; } if (flag[t]==-1||al[t]==0) break; int k1=t,k2=abs(pre[k1]),alph=al[t]; while (1) { if (e[k2][k1].c>0) e[k2][k1].f+=alph; else e[k1][k2].f-=alph; if (k2==s) break; k1=k2; k2=abs(pre[k1]); } } int ans=0; for (int i=0;i<n;i++) ans+=e[s][i].f; return ans;}void doit(){ for (int i=0;i<N;i++) for (int j=0;j<N;j++) e[i][j].c=e[i][j].f=0; //add edges printf("%d\n",ford(s,t,N));}
//algo2: //Dinic//O(n*n*m)struct dinic{ int g[N],d[N],G[N]; int np[M],to[M],cap[M]; int n,s,t,e; void AddEdge(int x,int y,int z,int z2=0) { to[e]=y;cap[e]=z; np[e]=g[x];g[x]=e++; to[e]=x;cap[e]=z2; np[e]=g[y];g[y]=e++; } void init(int n) { this->n=n;e=0; memset(g,255,sizeof(g)); } int maxflow(int s,int t) { this->s=s; this->t=t; int ans=0; while (bfs()) {for(int i=0;i<n;i++)G[i]=g[i]; ans+=dfs(s,inf); } return ans; } bool bfs()//建立层次图 { memset(d,-1,sizeof(d)); queue<int > q; q.push(s); d[s]=0; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=g[x];~i;i=np[i]) { int y=to[i]; if(cap[i]&&d[y]==-1) { d[y]=d[x]+1; if(y==t)return 1; q.push(y); } } } return 0; } int dfs(int x,int sum) { if (x==t|| sum==0) return sum; int ss=sum,ret; for(int &i=G[x];~i;i=np[i]) //当前弧优化 { int y=to[i]; if(d[y]==d[x]+1&&(ret=dfs(y,min(sum,cap[i])))) { cap[i]-=ret; cap[i^1]+=ret; sum-=ret; if (!sum) break; } } return ss-sum; }}it;
SAP的两个优化:
GAP优化——如果一次重标号时,出现距离断层,则可以证明ST无可行流,此时则可以直接退出算法。
当前弧优化——为了使每次找增广路的时间变成均摊O(V),还有一个重要的优化是对于每个点保存“当前弧”:初始时当前弧是邻接表的第一条弧;在邻接表中查找时从当前弧开始查找,找到了一条允许弧,就把这条弧设为当前弧;改变距离标号时,把当前弧重新设为邻接表的第一条弧。
//algo3_1://SAP//O(n*n*m)struct SAP{ int g[N],d[N],G[N],pre[N],numd[N]; int np[M],to[M],cap[M]; int n,e,s,t; void AddEdge(int x,int y,int z,int z2=0) { to[e]=y;cap[e]=z; np[e]=g[x];g[x]=e++; to[e]=x;cap[e]=z2; np[e]=g[y];g[y]=e++; } void init(int n) { this->n=n; e=0;memset(g,255,sizeof(g)); } void bfs()//先用广度优先算出每个点的d值 { memset(numd,0,sizeof(numd)); for(int i=0; i<n; i++) numd[d[i]=n]++; d[t]=0; numd[n]--; numd[0]++; queue<int> Q;Q.push(t); while(!Q.empty()) { int v=Q.front();Q.pop(); int i=g[v]; while(i!=-1) { int u=to[i]; if(d[u]<n){i=np[i];continue;} d[u]=d[v]+1; numd[n]--;numd[d[u]]++; Q.push(u); i=np[i]; } } } int maxflow(int s,int t) { this->s=s; this->t=t; for(int i=0;i<n;i++) G[i]=g[i]; int max_flow=0; bfs(); int u=s ;//从源点搜一条到汇点的增广路 while(d[s]<n)//就算所有的点连成一条线源点的d值也是最多是n-1 { if(u==t)//如果找到一条增广路径 { int cur_flow=inf,neck;//找到那条瓶颈边 for(int from=s; from!=t; from=to[G[from]]) if(cur_flow>cap[G[from]]) { neck=from;cur_flow=cap[G[from]];} for(int from=s; from!=t; from=to[G[from]]) //修改增广路上的边的容量 { int tmp=G[from]; cap[tmp]-=cur_flow; cap[tmp^1]+=cur_flow; } max_flow+=cur_flow;//累加计算最大流 u=neck;//下一次搜索直接从瓶颈边的前一个节点搜起 } int i; for(i=G[u];~i; i=np[i]) //从当前点开始找一条允许弧 if(cap[i]&&d[u]==d[to[i]]+1)//如果找到跳出循环 break; if(i!=-1)//找到一条允许弧 { G[u]=i;//从点u出发的允许弧的地址 pre[to[i]]=u;//允许弧上下一个点的前驱为u u=to[i];//u变成下一个点继续搜直到搜出一条增广路 } else //如果没有搜到允许弧 { numd[d[u]]--; //d[u]将被修改所以numd[d[u]]减一 if(!numd[d[u]]) break; //如果没有点的d值为d[u]则不可能再搜到增广路结束搜索 G[u]=g[u]; //当前点的允许弧为第一条边 int tmp=n; for(int j=g[u]; ~j; j=np[j]) //搜与u相连的点中d值最小的 if(cap[j]&&tmp>d[to[j]])tmp=d[to[j]]; d[u]=tmp+1; //修改d[u] numd[d[u]]++; if(u!=s)u=pre[u];//从u的前驱搜,因为从u没有搜到允许弧 } } return max_flow; }}it;
//algo3_2://SAP//O(n*n*m)#define N 105int n,m,np,nc,st,ed;struct SAP{ int g[N][N];//直接在原图上操作了 int d[N],gap[N]; int n,s,t; void AddEdge(int x,int y,int z) { g[x][y]=z; } void init(int n) { this->n=n; memset(g,0,sizeof(g)); memset(gap,0,sizeof(gap)); memset(d,0,sizeof(d)); } int maxflow(int s,int t) { this->s=s;this->t=t; int ans=0; for(gap[0]=n;d[s]<n;) ans+=sap(s,inf); return ans; } int sap(int u,int flow) { if(u==t) return flow; int res=flow; for(int i=0;i<n;++i) if(g[u][i] && d[u]==d[i]+1) { int tmp=sap(i,min(res,g[u][i])); g[u][i]-=tmp,g[i][u]+=tmp; if(!(res-=tmp))return flow; } if(!--gap[d[u]])d[s]=n; ++gap[++d[u]]; return flow-res; }}it;
//algo4: //ISAP //O(n*n*m) //ISAP 的一个所谓 GAP 优化: //由于从 s 到 t 的一条最短路径的顶点距离标号单调递减,且相邻顶点标号差严格等于1, //因此可以预见如果在当前网络中距离标号为 k (0 <= k < n) 的顶点数为 0, //那么可以知道一定不存在一条从 s 到 t 的增广路径,此时可直接跳出主循环。 struct ISAP { struct Edge { int from,to,cap,flow; Edge(){} Edge(int a,int b,int c,int d):from(a),to(b),cap(c),flow(d){} }; int n,m,s,t;//结点数,边数(含反向弧),源点,汇点 vector<Edge> edges;//边表,edges[e]&edges[e^1]互为反向弧 vector<int> G[N];//邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[N];//BFS使用 int d[N];//从起点到i的距离 int cur[N];//当前弧下标 int p[N];//可增广路上的上一条弧 int num[N];//距离标号计数 void AddEdge(int from,int to,int cap)//重边不影响 { edges.push_back(Edge(from,to,cap,0)); edges.push_back(Edge(to,from,0,0));//容量为0,表示反向弧 m=edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } void init(int n) { this->n=n; for(int i=0;i<n;++i) G[i].clear(); edges.clear(); } void BFS()//反向 { for (int i=0;i<n;i++) d[i]=n+10; //!!! memset(vis,0,sizeof(vis)); queue<int> Q; Q.push(t); d[t]=0; vis[t]=1; while(!Q.empty()) { int x=Q.front(); Q.pop(); for(int i=0; i<G[x].size(); ++i) { Edge& e=edges[G[x][i]^1]; if(!vis[e.from]&&e.cap>e.flow) { vis[e.from]=1; d[e.from]=d[x]+1; Q.push(e.from); } } } } int Augment() { int x=t,a=INF; while(x!=s) { Edge& e=edges[p[x]]; a=min(a,e.cap-e.flow); x=edges[p[x]].from; } x=t; while(x!=s) { edges[p[x]].flow+=a; edges[p[x]^1].flow-=a; x=edges[p[x]].from; } return a; } int maxflow(int s,int t)//结点数 { this->s=s,this->t=t; int flow=0; BFS(); memset(num,0,sizeof(num)); for(int i=0;i<n;++i) if (d[i]!=n+10)++num[d[i]];//!!! int x=s; memset(cur,0,sizeof(cur)); while(d[s]<n) { if(x==t) { flow+=Augment(); x=s; } int ok=0; for(int i=cur[x];i<G[x].size();++i) { Edge& e=edges[G[x][i]]; if(e.cap>e.flow&&d[x]==d[e.to]+1)//Advance { ok=1; p[e.to]=G[x][i]; cur[x]=i; x=e.to; break; } } if(!ok)//Retreat { int m=n-1; for(int i=0;i<G[x].size();++i) { Edge& e=edges[G[x][i]]; if(e.cap>e.flow) m=min(m,d[e.to]); } if(--num[d[x]]==0) break;//gap优化 num[d[x]=m+1]++; cur[x]=0; if(x!=s) x=edges[p[x]].from; } } return flow; } }it;
algorithm Improved-Shortest-Augmenting-Path f <-- 0 从终点 t 开始进行一遍反向 BFS 求得所有顶点的起始距离标号 d(i) i <-- s while d(s) < n do if i = t then // 找到增广路 Augment i <-- s // 从源点 s 开始下次寻找 if Gf 包含从 i 出发的一条允许弧 (i,j) then Advance(i) else Retreat(i) // 没有从 i 出发的允许弧则回退 return fprocedure Advance(i) 设 (i,j) 为从 i 出发的一条允许弧 pi(j) <-- i // 保存一条反向路径,为回退时准备 i <-- j // 前进一步,使 j 成为当前结点procedure Retreat(i) d(i) <-- 1 + min{d(j):(i,j)属于残量网络Gf} // 称为重标号的操作 if i != s then i <-- pi(i) // 回退一步procedure Augment pi 中记录为当前找到的增广路 P delta <-- min{rij:(i,j)属于P} 沿路径 P 增广 delta 的流量 更新残量网络 Gf
~~~~~~~~~~~~~~~~~~~~~~~~~~~增广路与预流推进的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~
定义e(u)为节点u上的余流,实际上我们并不需要计算各个h(v),只需将h(s)标为n,其余标为0,算法也能逐步计算出各个点的h。网络流中的节点(除s, t)仅需要满足流入量不小于流出量。其中流入量大于流出量的节点,我们称之为活动节点。预流推进算法就是不断地将活动结点,变为非活动结点,使得预流成为可行流。具体分为两个方法:
①压入操作,选出一个活动顶点u。并依次判断残余网络G中每条边(u, v),若h(u) = h(v) + 1,则顺着这里条边推流,流量变化量df(u,v)=min(e[u],cf(u,v)),同时更新相应的e(u),e(v);
②重标记操作,选出一个活动结点。如果对于残余网络G中每条边(u, v),都有h(v)大于或等于h(u),则需要对u进行重新标号,h(u) = min{h(v) + 1};
具体实现又可以根据对活跃点的推进过程分为:
①一般的预流推进算法(Generic Preflow-Push Algorithm),在残余网络中,维护一个预流,不断对活跃点执行push操作,或者relable操作来重新调整这个预流,直到不能操作。时间复杂度为O(V*E*E);
②先进先出预流推进算法,在残余网络中,以先进先出队列维护活跃点,时间复杂度为O(V*V*V);
③最高标号预流推进算法(Highest Preflow-Push Algorithm),在残余网络图中,每次检查最高标号的活跃点,需要用到优先队列,时间复杂度为O(V*V*E^0.5);
//algo5://HLPP//O(n*n*m^(1/2))#define N 105int d[N];struct HLPP{ int e[N]; int g[N][N]; struct node{ int index; friend bool operator < (const node& a, const node& b) {return d[a.index] > d[b.index];} }; int n,s,t; void Relabel(int u) { int m1=inf; for(int i=0; i<n; ++i) if(g[u][i] && d[i] < m1)m1 =d[i]; d[u]=m1+1; } void AddEdge(int x,int y,int z) { g[x][y]+=z; } void init(int n) { this->n=n; memset(g,0,sizeof(g)); } int maxflow(int s,int t) { node no; priority_queue<node> Q; memset(e,0,sizeof(e)); memset(d,0,sizeof(d)); d[s]=n; for(int i=0;i<n;++i) if(g[s][i]) { int df=g[s][i]; e[s] -= df; e[i] += df; g[s][i] -= df; g[i][s] += df; no.index = i; if(i!=s&& i!=t) Q.push(no); } while(!Q.empty()) { no=Q.top();Q.pop(); int u=no.index; while(e[u]) { int i; for(i = 0; i < n; ++i) if(g[u][i] && d[u] > d[i])break; if(i == n) Relabel(u); for(int i=0; i<n; ++i) if(d[u]==d[i] + 1 && g[u][i]) { int df = min(e[u], g[u][i]); g[u][i] -= df; g[i][u] += df; e[u] -= df; e[i] += df;; no.index = i; if(e[i] > 0&&i!=s &&i!=t) Q.push(no); } } } return e[t]; }}it;
~~~~~~~~~~~~~~~~~~~~~~~~~~~最大流与最小费用最大流间的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~
struct MCMF{ struct Edge { int from,to,cap,flow,cost; Edge(){} Edge(int a,int b,int c,int d,int e):from(a),to(b),cap(c),flow(d),cost(e){} }; int n,m,s,t; vector<Edge> edges; vector<int> G[N]; int inq[N],d[N],p[N],a[N]; queue<int> Q; void init(int n) { this->n=n; for(int i=0;i<n;++i) G[i].clear(); edges.clear(); } void AddEdge(int from,int to,int cap,int cost) { //printf("%d %d %d %d\n",from,to,cap,cost); edges.push_back(Edge(from,to,cap,0,cost)); edges.push_back(Edge(to,from,0,0,-cost)); m=edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool SPFA(int s,int t,int & flow,int & cost) { memset(d,0x3f,sizeof(d)); memset(inq,0,sizeof(inq)); d[s]=0;inq[s]=1;p[s]=0;a[s]=inf; Q.push(s); while(!Q.empty()) { int u=Q.front();Q.pop(); inq[u]=0; for(int i=0;i<G[u].size();++i) { Edge& e=edges[G[u][i]]; if(e.cap>e.flow&&d[e.to]>d[u]+e.cost) { d[e.to]=d[u]+e.cost; p[e.to]=G[u][i]; a[e.to]=min(a[u],e.cap-e.flow); if(!inq[e.to]) {Q.push(e.to);inq[e.to]=1;} } } } if(d[t]==inf) return false; flow+=a[t];//固定流量k,检查,若flow+a>=k,只增加k-flow个单位 cost+=d[t]*a[t]; int u=t; while(u!=s) { edges[p[u]].flow+=a[t]; edges[p[u]^1].flow-=a[t]; u=edges[p[u]].from; } return true; } int mincost(int s,int t) { int flow=0,cost=0; while(SPFA(s,t,flow,cost)); return cost; }}it;
- 【网络流】网络扩容
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 【网络流】
- 【网络流】
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 网络流
- 回收站文件清理了还能找回来吗
- ZOJ 2334 HDU 1512 Monkey King
- QUI框架V3.3版本正式发布!
- linux学习笔记
- Android PullToRefresh 下拉刷新,上拉更多,支持ScrollView,ListView,可方便拓展GridView,WebView等
- ##网络流##
- LESS CSS 框架简介
- 写歌低11首《桃花》
- C++声明和定义的区别
- 研调:2018年40%智能手机将用柔性面板
- 广播与多播的实现
- 使用iWebOffice实现电子签章
- 2013-12-IOS 获取最新设备型号方法
- ssh不能连接 提示WARNING: POSSIBLE DNS SPOOFING DETECTED!处理方法