咕咕咕-HLPP算法

hlpp(歡樂婆婆)算法總結

忽然發現咕了很久(X)

  emm先大概說一下,hlpp是針對網絡流算法的一種複雜度更優的算法,基於預流推動(即模擬) 複雜度上界爲 n2根號m 且跑不滿html

(因此學會了它,能夠解決絕大部分dinic能解決的問題,以及絕大部分dinic不能解決的問題node

先把之前的dinic算法放一下吧c++

你谷P3376 網絡最大流模板算法

#include<bits/stdc++.h>
#define re register
using namespace std;
const int maxxx=(1ll<<31)-1;
inline int read()
{
    register int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
struct node
{
    int to,nxt,dis;
}e[210000];
int head[100010],cur[100010],cnt=-1;
void add(int u,int v,int w)
{
    e[++cnt].to=v;
    e[cnt].dis=w;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}
int d[100010],n,m,s,t;
bool bfs()
{
    queue<int> q;
    q.push(s);
    memset(d,-1,sizeof(d));
    d[s]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i!=-1;i=e[i].nxt)
        {
            int v=e[i].to;
            if(e[i].dis && d[v]==-1)
            {
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
    return (d[t]!=-1);
}
int dfs(int u,int flow)
{
    if(u==t) return flow;
    int tmp=0,newflow;
    for(int i=cur[u ];i!=-1;i=e[i].nxt)
    {
        int v=e[i].to;
        if(d[v]==d[u]+1 && e[i].dis)
        {
            newflow=dfs(v,min(flow,e[i].dis));
            flow-=newflow;
            e[i].dis-=newflow;
            tmp+=newflow;
            e[i^1].dis+=newflow;
            if(!flow) break;
        }
    }
    if(!tmp) d[u]=-1;
    return tmp;
}
void dinic()
{
    int maxflow=0;
    while(bfs())
    {
        for(re int i=1;i<=n;++i) cur[i]=head[i];
        maxflow+=dfs(s,maxxx);
    }
    printf("%d\n",maxflow);
}
int main()
{
    memset(head,-1,sizeof(head));
    n=read(); m=read(); s=read(); t=read();
    for(re int i=1;i<=m;++i)
    {
        int x,y,z;
        x=read(); y=read(); z=read();
        add(x,y,z); add(y,x,0);
    }
    dinic();
}

  Dinic算法的基本思想就是找增廣路,去進行流量增廣的一種(貪心?)算法。而後爲了可以保證正確性,反向給本身反悔的機會數組

(咱也不知道模擬費用流啥的算法是啥網絡

  可是這個HLPP算法呢,並非基於增廣路算法的網絡流,而是基於另外一種(貪心?)算法,去進行預流推動優化

(循環屁放了第二遍)spa

  基本思想:

  先說一下基本思想吧: 這個算法是容許每一個點儲存一個流量的(超額流),不過要保證到了最後除了源點匯點之外的點超額流爲0(達到動態平衡(笑))code

  因此爲了每一個節點儲存的超額流可以推送出去,引入了高度的概念(愈來愈像模擬了)htm

  同時在引入了高度後,就能夠避免兩個節點互相推送超額流的狀況

  不過,想想,爲何,在現實生活中的水坑,就是一個例子,咱們把超額流推給低的節點,結果有的水流就積攢到了這個水坑裏,由於四周都比他高,而可憐的水坑

  承受着巨大的超額流結果推送不出去,咱們就死循環了。

  因此咋辦呢?  要擡高這樣的點的高度(廢話

  這個操做叫作重貼標籤

  (什麼最小頂標和啊什麼的我是徹底不會

  具體實現過程:

  咱們使用e[i]表示一個點的超額流,h[i]表示一個點的高度

  從匯點開始進行bfs賦值高度(這裏與網絡流的分層圖不一樣,爲了保證能夠流到匯點,必須讓高度遞增

  每次處理高度最高且超額流不爲0的點(用優先隊列維護) ,並對其進行推流操做把全部能推的都儘可能推出去,不用擔憂正確性,由於...

 

 

  就算不對也可讓人家再退回來鴨!

  因此算法的正確性一目瞭然,和增廣路的貪心反悔是相同的

    接下來若是e[u]仍是不等於0,就要進行重貼標籤,去擡高高度繼續等待推流

   最後若是除了源點匯點,其餘超額流都是0,那麼說明方案合法,此時匯點的超額流就是原圖最大流

  優化:

  爲了這個優秀的算法在實現時能徹底優於增廣路算法,加入一個船新優化(該優化比增廣路中當前弧優化更優

  GAP優化

  咱們還能夠發現若是一個點v在被重貼標籤之後,若是它原來的高度已經沒有其它點,那麼高於它的點必定不能將流量推送到t了。

  因此咱們能夠將高度大於h[v]且小於n+1的點高度設置爲n+1,以便儘快將流量推送給s。

  對於如何判斷這個高度已經沒有其它節點,能夠和ISAP同樣用一個gap數組來計數,這就是HLPPgap優化。

  具體實現:

  

#include<bits/stdc++.h>
#define re register
#define il inline
#define inc(i,j,k) for(re int i=j;i<=k;++i)
#define ra(i,u) for(re int i=head[u];i!=-1;i=a[i].nxt)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxm=120010;
const int maxn=2010;
struct node
{
    int to,nxt,flow;
}a[maxm<<1];
int head[maxn],gap[maxn],h[maxn],e[maxn];
bool vis[maxn];
int cnt=-1,n,m,st,ed;
struct cmp {il bool operator () (int x,int y)const{return h[x]<h[y];}};
priority_queue<int,vector<int>,cmp> pq;
queue<int> q;
il void add(int u,int v,int w)
{
    a[++cnt].to=v;
    a[cnt].nxt=head[u];
    a[cnt].flow=w;
    head[u]=cnt;
}
il bool bfs()
{
    memset(h,inf,sizeof(h));
    h[ed]=0;
    q.push(ed);
    while(!q.empty())
    {
        int t=q.front();
        q.pop();
        ra(i,t)
        {
            int v=a[i].to;
            if(a[i^1].flow && h[v]>h[t]+1)
            {
                h[v]=h[t]+1;
                q.push(v);
            }
        }
    }
    return h[st]!=inf;
}
il void push(int u)
{
    ra(i,u)
    {
        int v=a[i].to;
        if((a[i].flow) && (h[v]+1==h[u]))
        {
            int df=min(e[u],a[i].flow);
            a[i].flow-=df;
            a[i^1].flow+=df;
            e[u]-=df;
            e[v]+=df;
            if((v!=st)&&(v!=ed)&&(!vis[v]))
            {
                pq.push(v);
                vis[v]=1;
            }
            if(!e[u])break;
        }
    }
}
il void relabel(int u)
{
    h[u]=inf;
    ra(i,u)
    {
        int v=a[i].to;
        if((a[i].flow)&&(h[v]+1<h[u]))h[u]=h[v]+1;
    }
}
inline int hlpp()
{
    if(!bfs())return 0;
    h[st]=n;
    memset(gap,0,sizeof(gap));
    inc(i,1,n) if(h[i]!=inf)gap[h[i]]++;
    ra(i,st)
    {
        int v=a[i].to;
        if(int f=a[i].flow)
        {
            a[i].flow-=f;a[i^1].flow+=f;
            e[st]-=f;e[v]+=f;
            if(v!=st&&v!=ed&&!vis[v])
            {
                pq.push(v);
                vis[v]=1;
            }
        }
    }
    while(!pq.empty())
    {
        int t=pq.top();pq.pop();
        vis[t]=0;push(t);
        if(e[t])
        {
            gap[h[t]]--;
            if(!gap[h[t]])
            {
                inc(v,1,n)
                {
                    if(v!=st&&v!=ed&&h[v]>h[t]&&h[v]<n+1)
                    {
                        h[v]=n+1;
                    }
                }
            }
            relabel(t);gap[h[t]]++;
            pq.push(t);vis[t]=1;
        }
    }
    return e[ed];
}
signed main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d%d%d",&n,&m,&st,&ed);
    inc(i,1,m)
    {
        int x,y;
        ll f;
        scanf("%d%d%lld",&x,&y,&f);
        add(x,y,f);
        add(y,x,0);
    }
    ll maxf=hlpp();
    printf("%lld",maxf);
    return 0;
}

這是我照着題解一點一點寫的,例題的話等之後再更(窩就是太弱了

最大流——預流推動

相關文章
相關標籤/搜索