poj 2391 网络流 拆点 Floyd最短路

网上的报告基本都是

拆分结点(为什么这么做不是完全明白,囧)

二分查找最大距离,网络流计算最大流与总的牛的头数比较

思路比较清晰,最大流用SAP算法

#include <iostream>
#include <vector>
#include <map>
#include <list>
#include <set>
#include <deque>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <cstdio>
#include <iomanip>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
#include <queue>
using namespace std;

///宏定义
const __int64  INF = 100000000000000;
const int MAXN = 500;
///全局变量 和 函数


int f, p;//顶点数(牛棚数) 和 边数
__int64 len[MAXN][MAXN]; //保存路径中的边的值,即传送时间 
__int64 maze[MAXN][MAXN];//残留网络
__int64 ans;//最终答案
int s, t, vnum, total;
struct node
{
	int cur, max; //当前的牛的数目 最大容纳的牛的数目
}q[300]; //顶点信息

void Floyd()
{
	int i, j, k;
	for (k = 1; k <= f; k++)
	{
		for (i = 1; i <= f; i++)
		{
			for (j = 1; j <= f; j++)
			{
				if (len[i][j] > len[i][k] + len[k][j])
				{
					len[i][j] = len[i][k] + len[k][j];
				}
			}
		}
	}
}
//
//SAP 算法
#define CLR(arr, what) memset(arr, what, sizeof(arr))
#define min(a,b) (a < b) ? a : b 
int pre[MAXN], cur[MAXN]; //前驱、后继
int gap[MAXN]; //层结点数(用于间隙优化)
int dis[MAXN]; //到汇点的最少弧的数目
int SAP(int s, int t, int n) //源点、汇点、结点数
{
	CLR(gap, 0); CLR(cur, 0); CLR(dis, 0);
	int u = pre[s] = s, maxflow = 0, v;
	__int64  aug = INF;
	gap[0] = n;
	while(dis[s] < n)
	{
		bool flag = false;
		for(v = cur[u]; v <= n; ++v) //寻找允许弧
		{
			if(maze[u][v] > 0 && dis[u] == dis[v] + 1)
			{
				flag = true;
				break;
			}
		}
		if(flag) //找到允许弧
		{
			pre[v] = u;
			cur[u] = v;
			aug = min(aug, maze[u][v]);
			u = v;
			if(v == t) //找到完整增广路
			{
				maxflow += aug;
				for(v = t; v != s; v = pre[v]) //更新残留网络
				{
					maze[pre[v]][v] -= aug; //正向边
					maze[v][pre[v]] += aug; //反向边
				}
				aug = INF, u = s; //重新从源点寻找
			}
		}
		else //找不到允许弧
		{
			int mindis = n;
			for(v = 1; v <= n; ++v) //重新标号
			{
				if(maze[u][v] && mindis > dis[v])
				{
					cur[u] = v;
					mindis = dis[v];
				}
			}
			if(--gap[dis[u]] == 0) //更新断层 + 判断是否断层(间隙优化)
				break;
			gap[dis[u] = mindis + 1]++; //更新断层
			u = pre[u]; //当前弧优化
		}
	}
	return maxflow;
}

//
void cal()
{
	int i, j, k;
	__int64 l = 0, r = INF, mid;
	while (l < r)
	{
		mid = (l + r) / 2;
		memset(maze, 0, sizeof(maze));

		//拆分每一个点
		//从超级源点到该点的可用流量为当前顶点的牛的头数
		//从顶点到超级汇点的可用流量为当前的最大的容量
		//构造网络流图
		for (i = 1; i <= f; i++)
		{
			maze[s][i] = q[i].cur;
			maze[i][i + f] = INF;
			maze[i + f][t] = q[i].max;
		}
		//如果该路径可用则构造一条从Aout到Bin的无穷通路
		for(i = 1; i <= f; i++)
		{
			for (j = 1; j <= f; j++)
			{
				if (i != j && mid >= len[i][j])
				{
					maze[i][j + f] = INF;
				}
			}
		}
		int temp = SAP(s, t, 2 * f + 2);
		if (temp == total)
		{
			ans = mid;
			r = mid;
		}
		else
			l = mid + 1;

	}

}
__int64 a[MAXN];
int main()
{
	///变量定义
	int i, j, k;
	///操作执行
	while (scanf("%d%d", &f, &p) != EOF)
	{
		//初始化传送时间,即边的值
		for (i = 1; i <= f; i++)
		{
			for (j = 1; j <= f; j++)
			{
				if (i == j)
					len[i][j] = 0;
				else
					len[i][j] = INF;
			}
		}

		s = 0;//超级源点
		t = 2 * f + 1;//超级汇点
		total = 0;//所有的牛的头数

		//输入每个顶点信息,并统计总的牛的头数
		for (i = 1; i <= f; i++)
		{
			scanf("%d%d", &q[i].cur, &q[i].max);
			total += q[i].cur;
		}

		for (i = 1; i <= p; i++)
		{
			int u, v;
			__int64 d;
			scanf("%d%d%I64d", &u, &v, &d);
			if (len[u][v] > d)
			{
				len[u][v] = len[v][u] = d;
			}
		}
		//Floyd算法求解所有源最短路径
		Floyd();

		ans = -1;
		//进行计算
		cal();
		
		printf("%I64d\n", ans);
	}

	///结束
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值