uva1335(贪心)

博客讨论了一个UVA在线判题系统的题目,涉及到在环形排列中分配不同礼物以避免相邻者拿到相同礼物的策略。当人数为偶数时,答案是相邻两人需求之和的最大值。对于奇数情况,采用二分法寻找最少礼物种类。程序实现通过记录每个人取礼物的范围来判断可行性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
又是看了题解,感觉最近做题都没什么想法出来:
有n个人围成一个圈,其中第i个人想要ri个不同的礼物。相邻的两个人可以聊天,炫耀自己的礼物。如果两个相邻的人拥有同一种礼物,则双方都会很不高兴。问:一共需要多少种礼物才能满足所有人的需要?假设每种礼物有无穷多个,不相邻的两个人不会一起聊天,所以即使拿到相同的礼物也没关系。


比如,一共有5个人,每个人都要一个礼物,则至少要3种礼物。如果把这3种礼物编号为1, 2, 3,则5个人拿到的礼物应分别是:1,2,1,2,3。如果每个人要两个礼物,则至少要5种礼物,且5个人拿到的礼物集合应该是:{1,2},{3,4},{1,5},{2,3},{4,5}。


思路:
如果n为偶数,那么答案为相邻的两个人的r值之和的最大值,即p=max{ri+ri+1}(i=1, 2, …, n),规定rn+1=r1。不难看出,这个数值是答案的下限,而且还可以构造出只用p种礼物的方案:对于一个编号为i的人,如果i为奇数,发编号为1~r的礼物ri;如果i为偶数,发礼物p-ri+1~p,请读者自己验证它是否符合要求。


n为奇数的情况比较棘手,因为上述方法不再奏效。这个时候需要二分答案:假设已知共有p种礼物,该如何分配呢?设第1个人的礼物是1~r1,不难发现最优的分配策略一定是这样的:编号为偶数的人尽量往前取,编号为奇数的人尽量往后取。这样,编号为n的人在不冲突的前提下,尽可能地往后取了rn样东西,最后判定编号为1的人和编号为n的人是否冲突即可。比如,n = 5,A = {2, 2, 5, 2, 5},p = 8时,则第1个人取{1, 2}, 第2个人取{3, 4}, 第3个人取{8, 7, 6, 5, 2}, 第4个人取{1, 3}, 第5个人取{8, 7, 6, 5, 4},由于第1个人与第5个人不冲突,所以p = 8是可行的。


程序实现上,由于题目并不要求输出方案,因此,只需记录每个人在[1~r1]的范围内取了几个,在[r1+1~n]的范围里取了几个(在程序中分别用left[i]和right[i]表示),最后判断出第n个人在[1~r1]里面是否有取东西即可。







#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N =100005;
int n, ans, l, r;
int gift[N];
int left[N];
int right[N];

bool judge(int total) {
	int x = gift[0];
	int y = total - gift[0];
	left[0] = gift[0];
	right[0] = 0;
	for(int i = 1 ; i < n ;i++) {
		if(i % 2 == 0) {
			right[i] = min(y - right[i - 1] , gift[i]);
			left[i] = gift[i] - right[i];	
		}
		else {
			left[i] = min(x - left[i - 1] , gift[i]);
			right[i] = gift[i] - left[i];
		}
	}
	return left[n - 1] == 0;
}
int main () {
	while(scanf("%d",&n)!= EOF && n) {
		for(int i = 0 ;i < n ; i++) {
			scanf("%d",&gift[i]);
		}
		ans = 0;
		for(int i = 0 ; i < n - 1;i++) {
			ans = max(ans , gift[i] + gift[i + 1]);
		}
		ans = max(ans , gift[0] + gift[n - 1]);
		if(n % 2 == 0)
			printf("%d\n",ans);
		else if(n == 1)
			printf("%d\n",gift[0]);
		else {
			l = ans;
			r = ans * 3;
			int mid;
			while(r > l) {
				mid = (l + r) / 2;
				if(judge(mid)) {
					r = mid;
				}
				else
					l = mid + 1;
			}
			printf("%d\n",l);
		}
	}	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值