日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

网络流专题(最大流与费用流)(一)

發布時間:2023/12/3 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网络流专题(最大流与费用流)(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

流量網絡
? 想要將一些水從S運到T,必須經過一些水站,鏈接水站的是管道,每條管道都有它的最大能容納的水量,求最多S到T能流多少流量。

基本概念
? 這是一個典型的網絡流模型。我們先了解網絡流的有關定義和概念。
? 若有向圖G=(V,E)滿足下列條件:

  • 有且僅有一個頂點S,它的入度為零,即d-(S) = 0,這個頂點S便稱為源點,或稱為發點。
  • 有且僅有一個頂點T,它的出度為零,即d+(T) = 0,這個頂點T便稱為匯點,或稱為收點。
  • 每一條弧都有非負數,叫做該邊的容量。邊(vi, vj)的容量用cij表示。
    ? 則稱之為網絡流圖,記為G = (V, E, C)

    1 .水流不遞增
    2 .不能存水
    3 流入多少水流出多少水
  • 可改進路(增廣路)

    殘留網絡

    割切


    找一個方法,把S和T斷開,所切截面為流量
    所能通過的水流為截面,
    所以最大流等于最小割

    流量算法的基本理論

    ? 定理1:對于已知的網絡流圖,設任意一可行流為f,任意一割切為(U, W),必有:V(f) ≤ C(U, W)。
    ? 定理2:可行流f是最大流的充分必要條件是:f的殘量網絡中不存在可改進路。
    ? 定理3:整流定理——
    如果網絡中所有的弧的容量是整數,則存在整數值的最大流。
    ? 定理4:最大流最小割定理——
    最大流等于最小割,即max V(f) = min C(U, W)。

    方法:

    Ford-Fulkson方法
    FF方法

    Edmond-Karp算法

    ? EK算法是FF方法中最簡單的一個,當然效率也就比較低
    ? EK算法的流程與ff方法完全相同,只是在尋找增廣路的時候使用了BFS
    ? 時間復雜度O(n*m^2)
    ? 空間復雜度O(n^2)(鄰接矩陣)
    ? 注意要退流哦!!!!
    復雜度太高基本不用

    代碼:

    #include<bits/stdc++.h> using namespace std; const int maxn=205; const int inf=0x7fffffff; int r[maxn][maxn]; //殘留網絡,初始化為原圖 bool visit[maxn]; int pre[maxn]; int m,n; bool bfs(int s,int t) //尋找一條從s到t的增廣路,若找到返回true {int p;queue<int > q;memset(pre,-1,sizeof(pre));memset(visit,false,sizeof(visit));pre[s]=s;visit[s]=true;q.push(s);while(!q.empty()){p=q.front();q.pop();for(int i=1;i<=n;i++){if(r[p][i]>0&&!visit[i]){pre[i]=p;visit[i]=true;if(i==t) return true;q.push(i);}}}return false; } int EdmondsKarp(int s,int t) {int flow=0,d,i;while(bfs(s,t)){d=inf;for(i=t;i!=s;i=pre[i])d=d<r[pre[i]][i]? d:r[pre[i]][i];for(i=t;i!=s;i=pre[i]){r[pre[i]][i]-=d;r[i][pre[i]]+=d;}flow+=d;}return flow; }int main() {while(scanf("%d%d",&m,&n)!=EOF){int u,v,w;memset(r,0,sizeof(r));///for(int i=0;i<m;i++){scanf("%d%d%d",&u,&v,&w);r[u][v]+=w;}printf("%d\n",EdmondsKarp(1,n));}return 0; }

    Sap算法

    ? sap算法是采用了距離標號的方法求最短增廣路, 而距離標號的思想來自于push__relable(壓入重標記)類算法
    ? 有興趣的同學可以去了解一下push__relable類算法

    int dfs(int x,int flow) {if(x==t)return flow;//如果是匯點,從x流到t的流量 int sum=0;//當前情況下x流出多少流量 for(int i=1;i<=m;i++) {if(g[x][i]&&(d[x]==d[i]+1))//g[x][i]:x可以流到i {int tmp=dfs(i,min(g[x][i],flow-sum));//受限于x到i的邊和剩余流量 g[x][i]-=tmp; g[i][x]+=tmp;//反向邊 sum+=tmp;//流出tmp流量 if(sum==flow)return sum;//如果所有流量用完了 //如果流量沒有用完,繼續循環 }}if(d[S]==-1)return sum;//出現斷層,已經斷開 cntd[d[x]]--;//cntd[i]第i層有多少點 d[x]層的點 if(!cntd[d[x]])//這層沒點了 ,說明出現斷層 d[S]=-1; //S最多在第m-1層,當出現在第-1層說明為不合理情況,要退出 ,相當于退出標記 d[x]++;//上升一層 cntd[d[x]]++;//層數點加1 return sum; }dfs(S,inf);

    Dinic算法

    ? Dinic算法是一個基于“層次圖”的時間效率優先的最大流算法。
    ? 層次圖是什么東西呢?層次,其實就是從源點走到那個點的最短路徑長度。
    ? 于是乎,我們得到一個定理:從源點開始,在層次圖中沿著邊不管怎么走,經過的路徑一定
    是終點在剩余圖中的最短路。

    ? Dinic算法也有類似于sap的為頂點定標的過程——通常用BFS實現……
    ? 每次從源點開始遍歷,如果一次bfs可以從源點到匯點整個網絡都會變成一個層次網絡
    ? 在一個分層網絡中, 只有a[i] = = a[j] or a[i] = = a[j] -1時<i,j>才有邊存在。
    ? 當且僅當a[i] == a[j] -1時,兩點之間的邊才被稱為有用邊,在接下來的尋找增廣路的過程中只會走這樣的有用邊。
    ? 因此,dfs的時候只要遍歷到匯點即可因為同一層和下一層都更新不了匯點了

    bool makelevel(int s,int t) {memset(d,0,sizeof(d));memset(q,0,sizeof(q));d[s]=1;//s的層級為1int l=0,r=0;q[r++]=s;while(l<r){int x=q[l++];if(x==t)return 1;//到匯點時退出for(int i=head[x];i;i=edge[i].next){if((d[edge[i].t]==0)&&(edge[i].w!=0))//容量為0的邊不能走&&該點的層級未被標記{q[r++]=edge[i].t;d[edge[i].t]=d[x]+1;//層級加一}}}return false; } int dfs(int x,int flow,int t)//從s到x流了多少流量 {if(x==t)return flow;int sum=0;for(int i=head[x];i;i=edge[i].next){if((edge[i].w!=0)&&(d[edge[i].t]==d[x]+1))//滿足層級要求{int tmp=dfs(edge[i].t,min(flow-sum,edge[i].w),t);edge[i].w-=tmp;edge[i^1].w+=tmp;sum+=tmp;if(sum==flow)return sum;}}return sum; } while(makelevel(1,m)) {ans+=dfs(1,INF,m); }

    完整代碼:

    #include <bits/stdc++.h> using namespace std; const long long inf=2005020600; int n,m,s,t,u,v; long long w,ans,dis[520010]; int tot=1,now[520010],head[520010]; struct node {int to,net;long long val; } e[520010];inline void add(int u,int v,long long w) {e[++tot].to=v;e[tot].val=w;e[tot].net=head[u];head[u]=tot;e[++tot].to=u;e[tot].val=0;e[tot].net=head[v];head[v]=tot; }inline int bfs() { //在慘量網絡中構造分層圖 for(register int i=1;i<=n;i++) dis[i]=inf;queue<int> q;q.push(s);dis[s]=0;now[s]=head[s];while(!q.empty()) {int x=q.front();q.pop();for(register int i=head[x];i;i=e[i].net) {int v=e[i].to;if(e[i].val>0&&dis[v]==inf) {q.push(v);now[v]=head[v];dis[v]=dis[x]+1;if(v==t) return 1;}}}return 0; }inline int dfs(int x,long long sum) { //sum是整條增廣路對最大流的貢獻if(x==t) return sum;long long k,res=0; //k是當前最小的剩余容量 for(register int i=now[x];i&&sum;i=e[i].net) {now[x]=i; //當前弧優化 int v=e[i].to;if(e[i].val>0&&(dis[v]==dis[x]+1)) {k=dfs(v,min(sum,e[i].val));if(k==0) dis[v]=inf; //剪枝,去掉增廣完畢的點 e[i].val-=k;e[i^1].val+=k;res+=k; //res表示經過該點的所有流量和(相當于流出的總量) sum-=k; //sum表示經過該點的剩余流量 }}return res; }int main() {scanf("%d%d%d%d",&n,&m,&s,&t);for(register int i=1;i<=m;i++) {scanf("%d%d%lld",&u,&v,&w);add(u,v,w);}while(bfs()) {ans+=dfs(s,inf); //流量守恒(流入=流出) }printf("%lld",ans);return 0; } //dinic時間復雜度:O(n^2 m).

    最小費用最大流

    ? 帶有費用的網絡流圖: G=(V,E,C,W) V:頂點; E:弧;C:弧的容量;W:單位流量費用。
    保證s到t流量最大的前提下,所需的費用最小,這就是最小費用最大流問題.

    基本思路:

    把弧<i,j>的單位費用w[i,j]看作弧<i,j>的路徑長度,每次找從源點s到匯點t長度最短(費用最小)的可增廣路徑進行增廣。

  • 最小費用可增廣路
  • 路徑s到t的長度即單位流量的費用。
    ? 其實就是把ek算法中的廣搜改成spfa……(因為存在負權邊所以得用spfa)
  • 代碼:

    bool spfa(int s,int t) {queue<int>q;for(int i=0;i<N;i++){dis[i]=INF;vis[i]=0;pre[i]=-1;}dis[s]=0;vis[s]=1;q.push(s);while(!q.empty()){int u=q.front();q.pop();vis[u]=1;for(int i=head[u];i;i=edge[i].next){int v=edge[i].to;if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost){dis[v]=dis[u]+edge[i].cost;pre[v]=i;if(!vis[v]){vis[v]=1;q.push(v);}}}}if(pre[t]==-1)return 0;return 1; } int minCostMaxflow(int s,int t,int &cost) {int flow=0;cost=0;while(spfa(s,t)){int Min=INF;for(int i=pre[t];i;i=pre[edge[i^1].to]){if(Min>edge[i].cap-edge[i].flow)Min=edge[i].cap-edge[i].flow;}for(int i=pre[t];i;i=pre[edge[i^1].to]){edge[i].flow+=Min;edge[i^1].flow-=Min;cost+=edge[i].cost;edge[i^1].flow+=edge[i].cost;edge[i].cost-=edge[i].cost;}flow+=Min;}return flow; } //返回的最大流,cost存的是最小費用

    洛谷模板題

    代碼:

    #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define ll long long #define inf 50000000 #define re register using namespace std; struct po {int from,to,dis,nxt,w; }edge[250001]; int head[250001],cur[1000001],dep[60001],n,m,s,t,u,num=-1,x,y,l,tot,sum,k,fa[10001]; int dis[5001],vis[5001],xb[5001],flow[5001]; inline int read() {int x=0,c=1;char ch=' ';while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();while(ch=='-')c*=-1,ch=getchar();while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=getchar();return x*c; } inline void add_edge(int from,int to,int w,int dis) {edge[++num].nxt=head[from];edge[num].from=from;edge[num].to=to;edge[num].w=w;edge[num].dis=dis;head[from]=num; } inline void add(int from,int to,int w,int dis) {add_edge(from,to,w,dis);add_edge(to,from,0,-dis); } inline bool spfa() {memset(dis,100,sizeof(dis));memset(vis,0,sizeof(vis));queue<int> q;while(!q.empty())q.pop();for(re int i=1;i<=n;i++){fa[i]=-1;}vis[s]=1;dis[s]=0;fa[s]=0;flow[s]=inf;q.push(s);while(!q.empty()){int u=q.front();q.pop();vis[u]=0;for(re int i=head[u];i!=-1;i=edge[i].nxt){int v=edge[i].to;if(edge[i].w>0&&dis[v]>dis[u]+edge[i].dis){dis[v]=dis[u]+edge[i].dis;fa[v]=u;xb[v]=i;flow[v]=min(flow[u],edge[i].w);if(!vis[v]){vis[v]=1,q.push(v);}}}}return dis[t]<inf; } inline void max_flow() {while(spfa()){int k=t;while(k!=s){edge[xb[k]].w-=flow[t];edge[xb[k]^1].w+=flow[t];k=fa[k];}tot+=flow[t];sum+=flow[t]*dis[t];} } int main() {memset(head,-1,sizeof(head));int d;n=read();m=read();s=read();t=read();for(re int i=1;i<=m;i++){x=read();y=read();l=read();d=read();add(x,y,l,d);}max_flow();cout<<tot<<" "<<sum; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的网络流专题(最大流与费用流)(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。