ZOJ 3229 Shoot the Bullet (有源汇有上下界的最大流)

Description

一位摄影师要在\(n\)天给\(m\)个女孩拍照,第\(x\)个女孩至少要拍\(G_x\)张,在第\(k\)天,摄影师有\(C_k\)个目标,\(T_{k1}\)\(T_{k2}\)\(\cdots\)\(T_{kCk}\),给目标\(T_{ki}\)拍的照片的数量要在\([L_{ki},R_{ki}]\)的范围内,第\(k\)天摄影师最多只能拍\(D_k\)张照片。问在不违反这些限制条件的情况下,摄影师最多可以拍的照片数量。

Input

多组用例,每组用例第一行给出两个整数\(n\)\(m\),表示拍照的天数和女孩的数量,第二行给出\(m\)个数,第\(i\)个数\(g_i\)表示给每个女孩至少要拍的照片数量,对于每一天,第一行给出两个整数\(c\)\(d\),表示目标的数量和这天最多拍的照片数,接下来的\(c\)行,每行给出三个数\(t\)\(l\)\(r\),表示对编号\(t\)的女孩拍的照片数量要在\([l,r]\)的范围内。

\(1 \leqslant n \leqslant 365\)\(1 \leqslant m \leqslant 1000\)\(1 \leqslant c \leqslant 100\)\(0 \leqslant d \leqslant 30000\)\(0 \leqslant t < m\)\(0 \leqslant l \leqslant r \leqslant 100\)

Output

对于每组用例,如果有可行方案,第一行输出最多能拍的照片数量,接下来每行输出每一天给每一个女孩拍的照片数量。如果有多组最优解,输出任意一组即可。如果没有可行方案,输出"-1"。

Sample Input

2 3
12 12 12
3 18
0 3 9
1 3 9
2 3 9
3 18
0 0 3
1 3 6
2 6 9

Sample Output

36
9
6
3
3
6
9

Solution

有源汇带上下界的最大流问题。建图,左侧\(n\)个点代表\(n\)天,右侧\(m\)个点代表\(m\)个女孩,若第\(i\)天要给第\(j\)个女孩拍照片,照片数量要在\([l,r]\)范围内,则代表第\(i\)天的点到第\(j\)天的点有一条容量范围位\([l,r]\)的边。添加源点\(s\)和汇点\(t\),源点\(s\)与代表\(n\)天的点之间建容量范围为\([0,d]\)的边,代表\(m\)个女孩的点与汇点\(t\)之间有容量范围为\([g,\infty]\)的边。这样一个有源汇带上下界的流网络就建好了。

下面就是有源汇带上下界的最大流问题的解决方法,首先判断是否有可行流,如果有可行流再寻找最大流。

  1. 判断可行流:从汇点\(t\)到源点建一条容量为无穷大的边把原来的网络变为无源汇的网络。对于每个结点\(i\)\(in[i]\)表示必须要流入\(i\)点的流量,\(out[i]\)表示必须要流出\(i\)点的流量,令\(du[i]=in[i]-out[i]\),表示这个点净的流入或流出量。另外建立一个超集源点\(ss\)和超级汇点\(tt\),遍历每个结点\(i\),如果\(du[i]>0\),就建一条从\(ss\)到i的容量为\(du[i]\)的边,如果\(du[i]<0\),就建一条从\(i\)\(tt\)的容量为\(-du[i]\)的边,原来的每条容量范围为\([l,r]\)的边就改为容量为\([0,r-l]\)的边,这样就变成了源点为\(ss\)汇点为\(tt\)普通的流网络。从\(ss\)\(tt\)跑最大流,如果满流,说明左右的必要弧已经流满,存在可行流。

  2. 求最大流:删掉超级源点\(ss\)和超级汇点\(tt\),从原来的源点\(s\)到原来的汇点\(t\)跑一遍最大流,得到的答案就是原来有源汇带上下界的网络的最大流。因为第一遍跑的必要流的流量存储在从t到s的反向弧中,在从\(s\)\(t\)跑最大流时,\(s\)\(t\)的边中流过了必要流,整体的网络中流过了自由流,因此整体的最大流就是原网络的最大流。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2e3 + 10;
const int M = 8e4 + 10;

struct Edge
{
	int to, c, next;
	Edge() {}
	Edge(int to, int c, int next) : to(to), c(c), next(next) {}
} edge[M];
int adj[N], tot;

void init()
{
	memset(adj, -1, sizeof(adj));
	tot = 0;
}

void add(int u, int v, int c)
{
	edge[tot] = Edge(v, c, adj[u]);
	adj[u] = tot++;
	edge[tot] = Edge(u, 0, adj[v]);
	adj[v] = tot++;
}

int level[N];
queue<int> q;
bool bfs(int s, int t)
{
	while (!q.empty()) q.pop();
	memset(level, -1, sizeof(level));
	level[s] = 0; q.push(s);
 	while (!q.empty())
	{
		int u = q.front(); q.pop();
		for (int i = adj[u]; i != -1; i = edge[i].next)
		{
			Edge &e = edge[i];
			if (e.c && level[e.to] == -1)
			{
				level[e.to] = level[u] + 1;
				if (e.to == t) return true;
				q.push(e.to);
			}
		}
	}
	return false;
}

int cur[N];
int dfs(int u, int t, int flow)
{
	if (u == t) return flow;
	for (int &i = cur[u]; i != -1; i = edge[i].next)
	{
		Edge &e = edge[i];
		if (e.c && level[e.to] > level[u])
		{
			int f = dfs(e.to, t, min(flow, e.c));
			if (f)
			{
				e.c -= f;
				edge[i ^ 1].c += f;
				return f;
			}
		}
	}
	return 0;
}

int dinic(int s, int t)
{
	int flow = 0;
	while (bfs(s, t))
	{
		memcpy(cur, adj, sizeof(adj));
        int f;
		while (f = dfs(s, t, INF)) flow += f;
	}
	return flow;
}

int du[N], lb[N], c[400], id[400][110];

int main()
{
	int n, m;
	while (~scanf("%d%d", &n, &m))
	{
		init();
		memset(du, 0, sizeof(du));
		int s = 0, t = n + m + 1;
		for (int i = 1; i <= m; i++)
		{
			int g;
			scanf("%d", &g);
			du[t] += g; du[n + i] -= g;
			add(n + i, t, INF);
		}
		for (int i = 1; i <= n; i++)
		{
			int d;
			scanf("%d%d", &c[i], &d);
			add(s, i, d);
			for (int j = 1; j <= c[i]; j++)
			{
				int t, l, r;
				scanf("%d%d%d", &t, &l, &r);
				t++;
				du[n + t] += l; du[i] -= l;
				id[i][j] = tot;
				lb[tot] = l;
				add(i, n + t, r - l);
			}
		}
		add(t, s, INF);
		int ss = n + m + 2, tt = n + m + 3;
		int sum = 0;
		for (int i = 0; i <= n + m + 1; i++)
		{
			if (du[i] > 0) add(ss, i, du[i]), sum += du[i];
			else if (du[i] < 0) add(i, tt, -du[i]);
		}
		int ans = dinic(ss, tt);
		if (ans < sum) { printf("-1\n\n"); continue; }
		adj[ss] = -1; adj[tt] = -1;
		ans = dinic(s, t);
		printf("%d\n", ans);
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= c[i]; j++)
				printf("%d\n", edge[id[i][j] + 1].c + lb[id[i][j]]);
		printf("\n");
	}
	return 0;
}

https://vjudge.net/problem/20756

posted @ 2017-08-16 16:09  达达Mr_X  阅读(188)  评论(0编辑  收藏  举报