有源汇有上下界最大流/最小流 配题(HDU 3157)

因为是有源汇所以设源点为 s,汇点为 t。

有源汇有上下界最大流:

  • 连接一条 t 指向 s 的边,容量为 INF。
  • 通过上述步骤,现在图变成了无源汇网络。
  • 引入超级源点 S,超级汇点 T。
  • 连接一条 S 到网络中所有d[i]>0的点的边,容量为 d[i]
  • 连接一条网络中所有d[i]<0的点到 S的边,容量为 -d[i]。(关于d的定义,参考无源汇有上下界可行流 配题(HDU 4940))。
  • 网络中边的容量为上界减去下界。
  • 计算一次超级源点 S 到超级汇点 T 的最大流,得出无源有上下界可行流是否存在。(关于可行流是否存在,参考无源汇有上下界可行流 配题(HDU 4940))
  • 在存在可行流的前提情况下,有源汇有上下界最大流 = 可行流 + s 到 t 的最大流,其代码过程是不删除 t -> s这条边,因为其反向边的流量就是可行流的大小,所以直接在计算过无源汇有上下界可行流的残留网络上计算 s 到 t 的最大流即可。

有源汇有上下界最小流:

  • 首先求最小流那么一定是有下界的,如果没有上界可以用INF来代替上界。
  • 连接一条 t 指向 s 的边,容量为 INF。
  • 通过上述步骤,现在图变成了无源汇网络。
  • 引入超级源点 S,超级汇点 T。
  • 连接一条 S 到网络中所有d[i]>0的点的边,容量为 d[i]
  • 连接一条网络中所有d[i]<0的点到 S的边,容量为 -d[i]
  • 网络中边的容量为上界减去下界。
  • 计算一次超级源点 S 到超级汇点 T 的最大流,得出无源有上下界可行流是否存在。
  • 在存在可行流的前提情况下,首先保存可行流,可行流大小为 t 到 s 这条边的反向边流量,那么,有源汇有上下界最小流 = 可行流流量 - t 到 s 的最大流(计算 t 到 s 的最大流的时候需要将 s 到 t 及其反向边的流量设置为 0)。

配题:HDU 3157,是一个有源汇有上下界最小流问题。

题意:(题目本意读一下就行了,电路板通电,让所有元件都工作,每个元件工作需要有一个最低电流,这里直接给抽象过来的题意)多组输入,给出N,M,分别表示除了起点“+”和终点“-”以外有 N 个点,有 M 个边,之后有 M 行,表示 M 条边,每一行有三个输入,p、n、i,表示有一条边从 p 指向 n 其容量下限为 i。求能让这个电路板上所有元件都有点的最小电量?没法都工作就输出impossible。

思路:没有什么抽象的东西,典型有源汇有上下界最小流问题,由于图中边没有流量上界,所以随便设置一个INF就可以了。如果没有可行流,就是输出“impossible”,如果有就输出最小流即可,具体思路上面已经给出,在代码中有充分体现。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
const int maxn = 200+10;
const int INF = 0x7fffffff;

int n, m;
int source, sink;
int F_source, F_sink;
int super_line_id;
int d[maxn];
int sb_node[maxn], sb_edge[maxn], depth[maxn];
struct EDGE
{
	int u, v;
	int flow;
	EDGE(int u, int v, int flow): u(u), v(v), flow(flow){}
};

vector<int>graph[maxn];
vector<EDGE>edge;

void init(int lft, int rht)
{
	memset(d, 0, sizeof(d));
	for (int i = lft; i <= rht; i++)
	{
		graph[i].clear();
	}
	edge.clear();
}

void add_edge(int u, int v, int flow)
{
	edge.push_back(EDGE(u, v, flow));
	edge.push_back(EDGE(v, u, 0));
	int cnt = edge.size();
	graph[u].push_back(cnt-2);
	graph[v].push_back(cnt-1);
}

int Min(int x, int y)
{
	if (x < y) return x;
	else return y;
}

bool spfa(int s, int t)
{
	queue<int>q;
	
	while (!q.empty()) q.pop();
	memset(depth, 0, sizeof(depth));
	
	depth[s] = 1;
	q.push(s);
	while (!q.empty())
	{
		int cur_node = q.front();
		q.pop();
		for (int i = 0; i < graph[cur_node].size(); i++)
		{
			int id = graph[cur_node][i];
			int next_node = edge[id].v;
			int flow = edge[id].flow;
			
			if (depth[next_node] == 0 && flow > 0)
			{
				depth[next_node] = depth[cur_node] + 1;
				sb_node[next_node] = cur_node;
				sb_edge[next_node] = id;
				q.push(next_node);
			}
		}
	}
	if (depth[t] == 0)
	{
		return false;
	}
	else
	{
		return true;
	}
}

int dinic(int s, int t)
{
	int res = 0;
	while (spfa(s, t))
	{
		int cut_flow = INF;
		for (int cur_node = t; cur_node != s; cur_node = sb_node[cur_node])
		{
			int id = sb_edge[cur_node];
			cut_flow = Min(cut_flow, edge[id].flow);
		}
		
		res += cut_flow;
		for (int cur_node = t; cur_node != s; cur_node = sb_node[cur_node])
		{
			int id = sb_edge[cur_node];
			edge[id].flow -= cut_flow;
			edge[id^1].flow += cut_flow;
		}
	}
	return res;
}

void show_flow(int s, int t)
{
//	bool vis[maxn];
//	queue<int>q;
//	
//	memset(vis, false, sizeof(vis));
//	while (!q.empty()) q.pop();
//	
//	vis[s] = true;
//	q.push(s);
//	
//	while (!q.empty())
//	{
//		int cur_node = q.front();
//		q.pop();
//		cout<< "cur_node:"<< cur_node<< endl;
//		for (int i = 0; i < graph[cur_node].size(); i++)
//		{
//			int id = graph[cur_node][i];
//			int next_node = edge[id].v;
//			int flow = edge[id].flow;
//			if (!vis[next_node])
//			{
//				cout<< "next_node:"<< next_node<< " flow:"<< edge[id^1].flow<< endl;
//				vis[next_node] = true;
//				q.push(next_node);
//			}
//		}
//	}
	for (int i = s; i <= t; i++)
	{
		for (int j = 0; j < graph[i].size(); j++)
		{
			int id = graph[i][j];
			int v = edge[id].v;
			if (v == i) continue;
			cout<< "<"<< i<< ","<< v<< ">"<< "flow="<< edge[id].flow<< " backflow="<< edge[id^1].flow<< endl;
		}
	}
}

int main()
{
	while (cin>> n>> m)
	{
		if (n == 0 && m == 0)
		{
			break;
		}
		int judge_flow = 0;
		init(0, n+5);
		source = 0; // pos
		sink = n+1; // neg
		F_source = n+2; // super source
		F_sink = n+3; // super sink
		
		for (int i = 1; i <= m; i++)
		{
			char s1[2], s2[2];
			int flow;
			int u, v;
			scanf("%s%s%d", s1, s2, &flow);
			if (s1[0] == '+')
			{
				u = source;
			}
			else if (s1[0] == '-')
			{
				u = sink;
			}
			else 
			{
				sscanf(s1, "%d", &u);
			}
			
			if (s2[0] == '+')
			{
				v = source;
			}
			else if (s2[0] == '-')
			{
				v = sink;
			}
			else
			{
				sscanf(s2, "%d", &v);
			}
			add_edge(u, v, INF-flow);
			d[u] -= flow;
			d[v] += flow;
		}
		add_edge(sink, source, INF);
		int mark = edge.size()-2;
		for (int i = source; i <= sink; i++)
		{
			if (d[i] > 0)
			{
				add_edge(F_source, i, d[i]);
				judge_flow += d[i];
			}
			else if (d[i] < 0)
			{
				add_edge(i, F_sink, -d[i]);
			}
		}
		int tmp = dinic(F_source, F_sink);
		int feasible_flow = edge[mark^1].flow;
		
		if (tmp != judge_flow)
		{
			cout<< "impossible"<< endl;
		}
		else
		{
			edge[mark].flow = 0;
			edge[mark^1].flow = 0;
			int max_flow = dinic(sink, source);
			cout<< feasible_flow - max_flow<< endl;	
		}
	}
	return 0;
} 
发布了331 篇原创文章 · 获赞 135 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/Triple_WDF/article/details/103386814