UVa 11167 Monkeys in the Emei Mountain 最大流,区间模型,输出方案

Monkeys inthe Emei Mountain
Input: Standard Input

Output: Standard Output

 

Xuexue is a prettymonkey living in the Emei mountain.She is extremely thirsty during time 2 and time 9 everyday, so she must drink 2units water during this period. She may drink water more than once, as long asthe total amount of water she drinks is exactly 2 - she never drinks more thanshe needs.  Xuexuedrinks 1 unit water in one time unit, so she may drinks from time 2 to time 4,or from 3 to 5, … , or from 7 to 9, or even drinks twice: first from 2 to 3,then from 8 to 9. But she can't drink from 1 to 3 since she's not thirsty attime 1, and she can't drink from 8 to 10, since she must finish at time 9.

 

There are many monkeys like Xuexue: we use a triple (v, a, b) to describe a monkey who is thirstyduring time a andb, and must drink exactly v units of water during that period.Every monkey drinks at the same speed (i.e. one unit water per unit time).

 

Unfortunately, people keep on doingsomething bad on the environment in Emei Mountain.Eventually, there are only oneunpolluted places for monkeys to drink. Further more, the place is so smallthat at most m monkeys can drinkwater together. Monkeys like to help each other, so they want to find a way tosatisfy all the monkeys' need. Could you help them?

 

Input

The input consists of several testcases. Each case contains two integers n andm (1 £ n £ 100, 1 £ m £ 5), followed by n lines of three integer numbers (v, a, b), where 0 £ v,a, b £ 50,000, a < b, v £ b - a. The last test case is followed by a single zero, which shouldnot be processed. There are at most 50 test cases.

 

Output

For each test case, print the case numberand whether there is a solution. If there is a solution, the following n lines describe the solution: onemonkey on a separate line. The first number kmeans the monkey drinks water for ktimes. The following k pairs (ai, bi) means the monkey drinksfrom aito bi (ai< bi). The pairs should besorted in ascending order, and ai should not be equal to ai+1 for 1£ i £ k-1 (otherwise these twodrinking periods could be combined). If more than one solution exists, any oneis acceptable. Note that there should be exactly one space between k and pairs (ai,bi), but nospace within each pair.

 

Sample Input                               Output forSample Input

3 1

2 2 9

2 3 5

3 5 8

2 1

4 5 9

4 8 12

5 2

2 1 3

2 3 5

2 5 7

2 1 7

4 2 6

0

Case 1: Yes

2 (2,3) (8,9)

1 (3,5)

1 (5,8)

Case 2: No

Case 3: Yes

1 (1,3)

1 (3,5)

1 (5,7)

2 (1,2) (6,7)

1 (2,6)


Original Author: Dong Zhou

Adapted by Rujia Liu(Problem description, Special judge)

Alternative Solution: HuayangGuo

题目链接:UVa 11167 Monkeys in the Emei Mountain

题目大意:

雪雪是一只猴子。它在每天的 2:00 —— 9:00之间非常渴,所以在这个期间它必须喝掉2个单位的水。它可以多次喝水,只要它喝水的总量是2.它从不多喝,在一小时内他只能喝一个单位的水。所以它喝水的时间段可能是2:00 ——4:00,或者3:00——5:00,或者7:00——9:00.甚至喝两次,第一次2:00——3:00,第二次8:00——9:00.但是它不能在1:00——3:00喝水,因为在1:00时它不渴,也不能在8:00——10:00喝水,因为9:00必须结束。

一共有n(n <= 100)只这样的猴子。我们用一个(v,a,b)(0 <= v,a,b <= 50000,a < b,v <= b - a)来描述一个在时间a~b之间口渴,并且必须在这个期间喝够v个单位水的猴子。所有猴子喝水的速度都是1小时喝1个单位的水。

现在的问题是只有一个地方可以让m只猴子同时喝水,需要作出一个能满足所有猴子的喝水需要的安排,问能否给出安排。多解时输出任意一组解即可。

题目分析:

抛开输出解来说,这就是一道典型的区间模型最大流。

很显然我们可以想到一种方法,建立超级源汇,对每只猴子 i 建边(s,i,v[ i ])其中 s 是源点,v[ i ]是猴子 i 需要喝水的总量。对每个时间点 x 建边(x,t,m)其中 t 是汇点,m表示这个时间点最多同时容纳m只猴子一起喝水。然后再对每个猴子 i 能喝水的时间 x 建边(i,x,1)表示猴子 i 可以在时间 x 喝一单位的水。很显然这样子跑一遍最大流后,如果满流则有解,否则无解。

但是!

由于时间轴有至多50000长,如果按照上述的建边必定超时。

而且我们可以注意到 n 至多100,那么可以划分出来的区间至多199个,我们可以通过离散化缩小数据规模!

首先将每个猴子喝水的时间区间的左右端点存入b[ ]数组中,排序并去除重复元素。接下来,对于每个区间b[ x ] - b[ x- 1 ](暂定区间编号为 x)如果包含于猴子 i 的喝水区间之内,则建边(i,x,b[ x ] - b[ x - 1 ])表示猴子 i 可以在区间 x 中喝至多(b[ x ] - b[ x - 1 ])的水,然后对每个猴子 i 建边(s,i,v[ i ])含义与先前一样,对于每个区间x,建边(x,t,m * (b[ x ] - b[ x - 1 ]))表示区间 x 至多能分配m * (b[ x ] - b[ x - 1 ])的水

然后跑一次最大流,如果满流则有解,否则无解。

接下来比较让人蛋疼的就是它的输出方案,那么对于每只猴子我们怎么知道它在哪几个区间喝了水?对着从这只猴子出发的反向边检查一下就好啦~,如果有流量说明属于这条边的正向边被流过(也就是说猴子去该边所指向的区间喝水了),且喝的水的容量就是反向边的流量。还有个要考虑的就是如果一个区间被好多猴子一起享用(用起来好怪啊,不要在意这些细节啦~),那么直接对这个区间不断的循环取就好了(就这么简单。。但是我还是被虐的很开心。。QUQ)

PS:求期末物理不挂科QAQ

代码如下:


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define clear(A, X) memset(A, X, sizeof A)
#define copy(A, B) memcpy(A, B, sizeof A)
using namespace std;
const int maxE = 1000000;
const int maxN = 1000;
const int maxQ = 1000000;
const int oo = 0x3f3f3f3f;
struct Edge{
	int v, n, c;
}edge[maxE];
struct  Node{
	int v, l, r;
}a[maxN];
int adj[maxN], cntE;
int Q[maxQ], head, tail;
int d[maxN], cur[maxN], pre[maxN], num[maxN];
int s, t, nv, n, m;
int b[maxN], ans[maxN], now[maxN], sum;
//b[]存离散化后的区间;
//now[]表示当前要取的区间应该从now[]开始;
//ans[]存要输出的区间,其中 i % 2 == 0 表示区间左端,i % 2 == 1 表示区间右端。
void addedge(int u, int v, int c){
	edge[cntE].v = v; edge[cntE].c = c; edge[cntE].n = adj[u]; adj[u] = cntE++;
	edge[cntE].v = u; edge[cntE].c = 0; edge[cntE].n = adj[v]; adj[v] = cntE++;
}
void rev_bfs(){
	clear(num, 0);
	clear(d, -1);
	d[t] = 0;
	num[0] = 1;
	head = tail = 0;
	Q[tail++] = t;
	while(head != tail){
		int u = Q[head++];
		for(int i = adj[u]; ~i; i = edge[i].n){
			int v = edge[i].v;
			if(~d[v]) continue;
			d[v] = d[u] + 1;
			Q[tail++] = v;
			num[d[v]]++;
		}
	}
}
int ISAP(){
	copy(cur, adj);
	rev_bfs();
	int flow = 0, u = pre[s] = s, i;
	while(d[s] < nv){
		if(u == t){
			int f = oo, neck;
			for(i = s; i != t; i = edge[cur[i]].v){
				if(f > edge[cur[i]].c){
					f = edge[cur[i]].c;
					neck = i;
				}
			}
			for(i = s; i != t; i = edge[cur[i]].v){
				edge[cur[i]].c -= f;
				edge[cur[i] ^ 1].c += f;
			}
			flow += f;
			u = neck;
		}
		for(i = cur[u]; ~i; i = edge[i].n) if(d[edge[i].v] + 1 == d[u] && edge[i].c) break;
		if(~i){
			cur[u] = i;
			pre[edge[i].v] = u;
			u = edge[i].v;
		}
		else{
			if(0 == (--num[d[u]])) break;
			int mind = nv;
			for(i = adj[u]; ~i; i = edge[i].n){
				if(edge[i].c && mind > d[edge[i].v]){
					cur[u] = i;
					mind = d[edge[i].v];
				}
			}
			d[u] = mind + 1;
			num[d[u]]++;
			u = pre[u];
		}
	}
	return flow;
}
void init(){
	clear(adj, -1);
	cntE = 0;
}
void build(){
	int cnt = 0, _cnt = 0;
	sum = 0;
	init();
	scanf("%d", &m);
	for(int i = 1; i <= n; ++i){
		scanf("%d%d%d", &a[i].v, &a[i].l, &a[i].r);
		b[cnt++] = a[i].l;
		b[cnt++] = a[i].r;
		sum += a[i].v;
	}
	sort(b, b + cnt);
	for(int i = 1; i < cnt; ++i) if(b[i] != b[_cnt]) b[++_cnt] = b[i];
	cnt = _cnt + 1;
	s = 0; t = n + cnt; nv = t + 1;
	for(int i = 1; i <= n; ++i) addedge(s, i, a[i].v);
	for(int i = 1; i < cnt; ++i) addedge(n + i, t, m * (b[i] - b[i - 1]));
	for(int i = 1; i < cnt; ++i) now[i] = b[i - 1];
	for(int i = 1; i <= n; ++i) for(int j = 1; j < cnt; ++j){
		if(a[i].l <= b[j - 1] && b[j] <= a[i].r) addedge(i, j + n, b[j] - b[j - 1]);
	}
}
void put(){
	int cnt, _cnt;
	for(int u = 1; u <= n; ++u){
		cnt = 0;
		for(int i = adj[u]; ~i; i = edge[i].n){
			int v = edge[i].v, c = edge[i ^ 1].c;
			if(c){//循环取区间
				ans[cnt++] = now[v - n];
				if(now[v - n] + c > b[v - n]){
					ans[cnt++] = b[v - n];
					ans[cnt++] = b[v - 1 - n];
					ans[cnt++] = b[v - 1 - n] + c - (b[v - n] - now[v - n]);
					now[v - n] = ans[cnt -1];
				}
				else now[v - n] += c, ans[cnt++] = now[v - n];
				if(now[v - n] == b[v - n]) now[v - n] = b[v - 1 - n];
			}
		}
		sort(ans, ans + cnt);
		_cnt = 0;
		for(int i = 1; i < cnt; ++i){
			if(ans[i] == ans[_cnt]) ans[_cnt] = ans[i + 1], ++i;
			else ans[++_cnt] = ans[i];
		}
		cnt = _cnt + 1;
		printf("%d", cnt / 2);
		for(int i = 0; i < cnt; i += 2) printf(" (%d,%d)", ans[i], ans[i + 1]);
		printf("\n");
	}
}
void work(){
	build();
	if(sum != ISAP()) printf("No\n");
	else{
		printf("Yes\n");
		put();
	}
}
int main(){
	int cas = 0;	
	while(~scanf("%d", &n) && n){
		printf("Case %d: ", ++cas);
		work();
	}
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值