图论中的超级源点和越级汇点(分别解决多起点,和多终点问题)

(本博主按:本文来自“阿阿阿安”的博客图论技巧 : 超级源点与超级汇点的建立_阿阿阿安的博客-CSDN博客,本人仅作为学习笔记使用,如需要转载,请联系该博主。)

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;
}

over~

--------------------- 
作者:阿阿阿安 
来源:CSDN 
原文:https://blog.csdn.net/qq_40772692/article/details/83090675 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值