学习笔记:超级源点与超级汇点的建立(Via. CSDN 阿阿阿安)

转载来源

什么是超级源点与超级汇点

  • 超级源点跟超级汇点是模拟出来的虚拟点,多用于图中 :

    <1> 同时有多个源点和多个汇点,建立超级源点和超级汇点

    <2> 同时有多个源点和一个汇点,建立超级源点

    <3> 同时有多个汇点和一个源点,建立超级汇点

  • 我们平时所做的算法多是适用于一个源点到一个汇点或者是一个源点到多个汇点的类型,但是如果出现多个源点对应多个汇点时,我们会不知所措。跑多遍算法?那样会TLE,换个思维,既然是从多个源点出发到多个汇点,我们能不能建立一个点来代替多个源点/汇点 的效果,而又不影响答案。

例题1 HDU2680 Choose the best route(Dijkstra + 建立超级源点)

  • 题意:给出你n个点,m条有向边,每条路都有时间花费,给你一个终点,多个起点,问你从起点到终点的最小时间花费是多少。

  • 分析:一眼看破最短路

    但是最短路Floyed算法O(n^3)会超时 , Dijkstra算法只能求单源点,这里是多源点单汇点。

    <1> 思路一:逆向跑Dijkstra,终点当起点。

    <2> 思路二:能到要跑多次Dijkstra?那是肯定不行的。既然是多个源点,我们要求这些源点里到汇点最短的。我们可以建立一个虚拟的源点,链接所有的起点,但是路径长度为0!然后跑从超级源点到汇点这(n+1)个点的最短距离即可!(妙,喵喵喵)

  • 代码:

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

例题2:Poj1459 Power Network(最大流 + 建立超级源点与超级汇点)

  • 题意:有三种节点:一种是发电厂,只产生电量不消耗电量;一种是用户,只消耗电量不产生电量;一种是变压器,既不产生也不消耗。给出n个节点,m条电路,每条电路都有电量限制。现在问你供电网络中传给用户的最大电量是多少。

  • 分析:多个发电厂相当于多个源点,多个用户相当于多个汇点。题意转化为求从多个源点到多个汇点的最大电量之和。

    建立超级源点0,超级汇点n+1,超级源点连接所有的源点,路径容量为发电厂的产电量;所有的汇点连接超级汇点,路径容量为用户的耗电量。然后跑一边从超级源点到超级汇点的最大流即可!

  • 代码:

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

猜你喜欢

转载自blog.csdn.net/weixin_37661548/article/details/87807009