洛谷P1344 [USACO4.4]追查坏牛奶Pollutant Control(网络流, 最大流最小割)

初学网络流:http://blog.csdn.net/wzw1376124061/article/details/55001639

最大流最小割:http://blog.csdn.net/wzw1376124061/article/details/71270835

题目描述

你第一天接手三鹿牛奶公司就发生了一件倒霉的事情:公司不小心发送了一批有三聚氰胺的牛奶。很不幸,你发现这件事的时候,有三聚氰胺的牛奶已经进入了送货网。这个送货网很大,而且关系复杂。你知道这批牛奶要发给哪个零售商,但是要把这批牛奶送到他手中有许多种途径。送货网由一些仓库和运输卡车组成,每辆卡车都在各自固定的两个仓库之间单向运输牛奶。在追查这些有三聚氰胺的牛奶的时候,有必要保证它不被送到零售商手里,所以必须使某些运输卡车停止运输,但是停止每辆卡车都会有一定的经济损失。你的任务是,在保证坏牛奶不送到零售商的前提下,制定出停止卡车运输的方案,使损失最小。

输入输出格式

输入格式:

第一行: 两个整数N(2<=N<=32)、M(0<=M<=1000), N表示仓库的数目,M表示运输卡车的数量。仓库1代 表发货工厂,仓库N代表有三聚氰胺的牛奶要发往的零售商。 第2..M+1行: 每行3个整数Si,Ei,Ci。其中Si,Ei表示这 辆卡车的出发仓库,目的仓库。Ci(0 <= C i <= 2,000,000) 表示让这辆卡车停止运输的损失。

输出格式:

两个整数C、T:C表示最小的损失,T表示在损失最小的前提下,最少要停止的卡车数。

输入输出样例

输入样例#1:
4 5
1 3 100
3 2 50
2 4 60
1 2 40
2 3 80
输出样例#1:
60 1

说明

题目翻译来自NOCOW。

USACO Training Section 4.4


题目思路:求割边最少的最小割。

方法一:

把边的权值全部乘以一个较大的数E再加1, 网络流最大流的答案为ans,那么ans/E, ans%E, 就是两问的答案了。

/*************************************************************************
    > Author: wzw-cnyali
    > Created Time: 2017/5/6 8:55:39
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

typedef long long LL;

typedef unsigned long long uLL;

#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)
#define EREP(i, a) for(register int i = (be[a]); i != -1; i = nxt[i])
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define mem(a, b) memset((a), b, sizeof(a))

template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }

template <class T>
T read(T sum = 0, T fg = 0)
{
	char c = getchar();
	while(c < '0' || c > '9') { fg |= c == '-'; c = getchar(); }
	while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); }
	return fg ? -sum : sum;
}

const int inf = 0x3f3f3f3f;

const int Size = 100000;

const int maxn = 100010;

const int maxm = 1000010;

int n, m;

namespace Dinic
{
	int Start, End;
	int be[maxm], to[maxm], nxt[maxm], e;
	LL w[maxm];
	int dis[maxn];

	void init()
	{
		mem(be, -1);
		e = 0;
	}

	void add(int x, int y, LL z)
	{
		to[e] = y;
		nxt[e] = be[x];
		be[x] = e;
		w[e] = z;
		e++;
	}

	int bfs()
	{
		queue <int> Q;
		Q.push(Start);
		mem(dis, -1);
		dis[Start] = 0;
		while(!Q.empty())
		{
			int x = Q.front();
			Q.pop();
			EREP(i, x)
			{
				int y = to[i];
				if(dis[y] == -1 && w[i])
				{
					dis[y] = dis[x] + 1;
					Q.push(y);
				}
			}
		}
		return dis[End] != -1;
	}

	LL dfs(int x, LL flow)
	{
		if(x == End) return flow;
		LL k;
		EREP(i, x)
		{
			int y = to[i];
			if(w[i] && dis[y] == dis[x] + 1 && (k = dfs(y, min(flow, w[i]))))
			{
				w[i] -= k;
				w[i ^ 1] += k;
				return k;
			}
		}
		return 0;
	}

	LL dinic()
	{
		LL ans = 0;
		LL ret = 0;
		while(bfs())
			while((ret = dfs(Start, inf)))
				ans += ret;
		return ans;
	}
}

LL E = 1001;

int main()
{
#ifndef ONLINE_JUDGE
	freopen("input.in", "r", stdin);
	freopen("output.out", "w", stdout);
#endif
	n = read<int>(), m = read<int>();
	Dinic::init();

	Dinic::Start = 1;
	Dinic::End = n;

	REP(i, 1, m)
	{
		int x = read<int>(), y = read<int>();
		LL z = read<LL>();
		Dinic::add(x, y, z * E + 1);
		Dinic::add(y, x, 0);
	}
	LL ans = Dinic::dinic();
	printf("%lld %lld\n", ans / E, ans % E);
    return 0;
}

方法二:

第一问,求最小割

第二问,由于要求删去的边最少

我们可以贪心一下:如果每次尽可能的割掉流量较大的边,就可以使得删掉的边尽量少了

判断一条割是否属于最小割的方法是:

删掉这条边后跑一边最大流,得到的答案+删去边的容量 = 最小割的答案,则说明这条边在割上。

每次若确定了一条边在割上就在原图中永远删掉并把最小割的答案重设为刚跑出来的答案

/*************************************************************************
    > Author: wzw-cnyali
    > Created Time: 2017/5/6 8:55:39
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>

using namespace std;

typedef long long LL;

typedef unsigned long long uLL;

#define REP(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
#define DREP(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)
#define EREP(i, a) for(register int i = (be[a]); i != -1; i = nxt[i])
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define mem(a, b) memset((a), b, sizeof(a))

template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }

template <class T>
T read(T sum = 0, T fg = 0)
{
	char c = getchar();
	while(c < '0' || c > '9') { fg |= c == '-'; c = getchar(); }
	while(c >= '0' && c <= '9') { sum = sum * 10 + c - '0'; c = getchar(); }
	return fg ? -sum : sum;
}

const int inf = 0x3f3f3f3f;

const int Size = 100;

const int maxn = 1010;

const int maxm = 100010;

struct node
{
	int x, y, z, id;
	friend bool operator < (node a, node b)
	{
		return a.z == b.z ? a.id < b.id : a.z > b.z;
	}
}edge[maxn];

int n, m;
int map[Size][Size];
int old[Size][Size];
vector <int> G[Size];

namespace Dinic
{
	int Start, End;
	int dis[maxn];

	int bfs()
	{
		queue <int> Q;
		Q.push(Start);
		mem(dis, -1);
		dis[Start] = 0;
		while(!Q.empty())
		{
			int x = Q.front();
			Q.pop();
			REP(i, 0, G[x].size() - 1)
			{
				int y = G[x][i];
				if(dis[y] == -1 && map[x][y])
				{
					dis[y] = dis[x] + 1;
					Q.push(y);
				}
			}
		}
		return dis[End] != -1;
	}

	int dfs(int x, int flow)
	{
		if(x == End) return flow;
		int k;
		REP(i, 0, G[x].size() - 1)
		{
			int y = G[x][i];
			if(map[x][y] && dis[y] == dis[x] + 1 && (k = dfs(y, min(flow, map[x][y]))))
			{
				map[x][y] -= k;
				map[y][x] += k;
				return k;
			}
		}
		return 0;
	}

	int dinic()
	{
		int ans = 0;
		while(bfs())
		{
			ans += dfs(Start, inf);
		}
		return ans;
	}

	void map_reset()
	{
		REP(i, 0, n) REP(j, 0, n)
			map[i][j] = old[i][j];
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("input.in", "r", stdin);
	freopen("output.out", "w", stdout);
#endif
	n = read<int>(), m = read<int>();

	Dinic::Start = 1;
	Dinic::End = n;

	REP(i, 1, m)
	{
		int x = read<int>(), y = read<int>(), z = read<int>();
		edge[i] = (node){x, y, z, i};
		map[x][y] += z;
		old[x][y] += z;
		G[x].push_back(y);
	}
	int min_cut = Dinic::dinic();
	printf("%d", min_cut);

	sort(edge + 1, edge + m + 1);
	int ans = 0;
	REP(i, 1, m)
	{
		Dinic::map_reset();
		int x = edge[i].x, y = edge[i].y, z = edge[i].z;
		map[x][y] -= z;
		int flow = Dinic::dinic();
		if(min_cut - flow == z)
		{
			ans++;
			old[x][y] -= z;
			min_cut = flow;
		}
	}
	printf(" %d\n", ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值