1. 程式人生 > >圖論中的超級源點和越級匯點(類似於幾何題中做延長輔助線解題的思路)

圖論中的超級源點和越級匯點(類似於幾何題中做延長輔助線解題的思路)

(本博主按:本文來自“阿阿阿安”的部落格https://blog.csdn.net/qq_40772692/article/details/83090675,本人僅作為學習筆記使用,如需要轉載,請聯絡該博主。)

1.什麼是超級源點與超級匯點

(1)超級源點跟超級匯點是模擬出來的虛擬點,多用於圖中 :

<1>同時有多個源點和多個匯點,建立超級源點和超級匯點

<2>同時有多個源點和一個匯點,建立超級源點

<3>同時有多個匯點和一個源點,建立超級匯點

(2)我們平時所做的演算法多是適用於一個源點到一個匯點或者是一個源點到多個匯點的型別,但是如果出現多個源點對應多個匯點時,我們會不知所措。跑多遍演算法?那樣會TLE,換個思維,既然是從多個源點出發到多個匯點,我們能不能建立一個點來代替多個源點/匯點 的效果,而又不影響答案。

2.例題1  HDU2680 Choose the best route(Dijkstra  + 建立超級源點)

(1)題意:給出你n個點,m條有向邊,每條路都有時間花費,給你一個終點,多個起點,問你從起點到終點的最小時間花費是多少。

(2)分析:一眼看破最短路

但是最短路Floyed演算法O(n^3)會超時 , Dijkstra演算法只能求單源點,這裡是多源點單匯點。

<1>思路一:逆向跑Dijkstra,終點當起點。

<2>思路二:能到要跑多次Dijkstra?那是肯定不行的。既然是多個源點,我們要求這些源點裡到匯點最短的。我們可以建立一個虛擬的源點,連結所有的起點,但是路徑長度為0!然後跑從超級源點到匯點這(n+1)個點的最短距離即可!(妙,喵喵喵)

(3)程式碼:

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int,int> P;
typedef long long LL;
const int maxn = 40000 + 7;
struct Edge{
    int to,next,val;
}edge[maxn];
int n,m,s,w,tot,head[1010],dist[1010];
bool vis[1010];
void addEdge(int a,int b,int c){
    edge[tot].to = b;edge[tot].val = c;edge[tot].next = head[a];head[a] = tot++;
}
void Dijkstra(int a){
     dist[a] = 0;
     memset(vis,0,sizeof(vis));
     priority_queue<P,vector<P> , greater<P> > que;
     que.push(P(0,a));
     while(!que.empty()){
        P p = que.top();
        que.pop();
        if(vis[p.second])continue;
        vis[p.second] = 1;
        for(int i = head[p.second];~i;i = edge[i].next){
            if(!vis[edge[i].to]&&dist[edge[i].to] > dist[p.second] + edge[i].val){
                dist[edge[i].to] = dist[p.second] + edge[i].val;
                que.push(P(dist[edge[i].to],edge[i].to));
            }
        }
     }
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&s)!=EOF){
        tot = 0;
        memset(head,-1,sizeof(head));
        memset(dist,INF,sizeof(dist));
        for(int i = 0;i<m;i++){
             int a,b,v;
             scanf("%d%d%d",&a,&b,&v);
             addEdge(a,b,v);//有向邊
        }
        scanf("%d",&w);
        for(int i = 0;i<w;i++){
            int p;
            scanf("%d",&p);
            addEdge(0,p,0);//超級源點0,連線所有源點,長度為0
        }
        Dijkstra(0);
        if(dist[s]==INF)printf("-1\n");
        else printf("%d\n",dist[s]);
    }
    return 0;
}

3.例題2:Poj1459 Power Network(最大流 + 建立超級源點與超級匯點)

(1)題意:有三種節點:一種是發電廠,只產生電量不消耗電量;一種是使用者,只消耗電量不產生電量;一種是變壓器,既不產生也不消耗。給出n個節點,m條電路,每條電路都有電量限制。現在問你供電網路中傳給使用者的最大電量是多少。

(2)分析:多個發電廠相當於多個源點,多個使用者相當於多個匯點。題意轉化為求從多個源點到多個匯點的最大電量之和。

建立超級源點0,超級匯點n+1,超級源點連線所有的源點,路徑容量為發電廠的產電量;所有的匯點連線超級匯點,路徑容量為使用者的耗電量。然後跑一邊從超級源點到超級匯點的最大流即可!

(3)程式碼:

#include <iostream>
//#include<bits/stdc++.h>
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 100 + 7;
struct Edge{
    int to,next,cap,flow;
}edge[maxn*maxn*2];
int n,np,nc,m,tot,head[maxn],dist[maxn],cur[maxn];
void addEdge(int a,int b,int c){
    edge[tot].to = b;edge[tot].next = head[a];edge[tot].cap = c;edge[tot].flow = 0;head[a] = tot++;
    edge[tot].to = a;edge[tot].next = head[b];edge[tot].cap = 0;edge[tot].flow = 0;head[b] = tot++;
}
bool BFS(int s,int t){//BFS分層圖
    memset(dist,0,sizeof(dist));
    dist[s] = 1;
    queue<int> que;
    que.push(s);
    while(!que.empty()){
        int u = que.front();
        que.pop();
        for(int i = head[u];~i;i = edge[i].next){
            int p = edge[i].to;
            if(!dist[p]&&edge[i].cap > edge[i].flow){
                dist[p] = dist[u] + 1;
                que.push(p);
            }
        }
    }
    if(!dist[t])return false;
    return true;
}
int DFS(int p,int e,int minFlow){//DFS找增廣路
    if(p==e||minFlow==0)return minFlow;
    int f = 0;
    for(int &i = cur[p];~i;i = edge[i].next){
        int v = edge[i].to;
        if(dist[v] == dist[p] + 1&&edge[i].cap > edge[i].flow){
             int dis = DFS(v,e,min(minFlow,edge[i].cap - edge[i].flow));
             minFlow-=dis;
             f+=dis;
             edge[i].flow+=dis;
             edge[i^1].flow-=dis;
             if(minFlow==0)break;
        }
    }
    return f;
}
int Dinic(int s,int t){//Dinic演算法
    if(s==t)return 0;
    int flow = 0;
    while(BFS(s,t)){
        for(int i = 0;i<=n+1;i++)cur[i] = head[i];//當前弧優化
        int ans = DFS(s,t,INF);
        flow+=ans;
    }
    return flow;
}
int main()
{
    while(scanf("%d%d%d%d",&n,&np,&nc,&m)!=EOF){
        tot = 0;
        memset(head,-1,sizeof(head));
        for(int i = 0;i<m;i++){
            int a,b,v;
            scanf(" (%d,%d)%d",&a,&b,&v);
            addEdge(a+1,b+1,v);//連線普通電路
        }
        for(int i = 0;i<np;i++){
            int a,v;
            scanf(" (%d)%d",&a,&v);
            addEdge(0,a+1,v);//所有源點連結超級源點,容量為供電量
        }
        for(int i = 0;i<nc;i++){
            int a,v;
            scanf(" (%d)%d",&a,&v);
            addEdge(a+1,n+1,v);//所有匯點連線超級匯點,容量為耗電量
        }
        int maxflow = Dinic(0,n+1);//跑最大流
        printf("%d\n",maxflow);
    }
    return 0;
}


 
--------------------- 
作者:阿阿阿安 
來源:CSDN 
原文:https://blog.csdn.net/qq_40772692/article/details/83090675 
版權宣告:本文為博主原創文章,轉載請附上博文連結!