LA3177 Beijing Guards (训练指南,贪心, 二分)

题意: n个人围成一圈,第i个人想要ri个不同的礼物,相邻的两个人礼物类型不能重复。
每种礼物不限量,求最少需要多少种礼物才行(1<=n<=100000)

训练指南, 37页。 分类讨论,n为偶数和奇数时。
n为偶数情况, p = max{r[i] + r[i + 1]} (1 <= i <= n) //记 r[n + 1] = r[1]
本题要点:
1、贪心来分析奇数情况
对应第一人,需要礼物 r[1], 用 r[1] 将 所有礼物分为两部分,
1 ~ r[1] 和 r[1] + 1 ~ p
int Left[MxaN]; //left[i] 表示第i个人 在 [1 ~ r1] 范围内取了多少个礼物
int Right[MxaN]; //right[i] 表示第i个人 在 [r1 + 1 ~ p] 范围内取了多少个礼物
假设第一个人 r[1] 个礼物全部取左边,也就是 left[1] = r[1], right[1] = 0;
后面的 人, i为奇数时候,尽量往右区间[r1 + 1 ~ p]取礼物,i为偶数时候,尽量往左[1 ~ r1]区间取礼物
如果最后能满足,left[n] == 0, 也就是 最后一人,取的礼物与 第一个人没有重复,说明 p 个礼物满足条件

2、二分查找 礼物总数 p, 找到最小的礼物总数

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MxaN = 100010;
int n;
int r[MxaN];
int Left[MxaN];		//left[i] 表示第i个人	在 [1 ~ r1] 范围内取了多少个礼物
int Right[MxaN];	//right[i] 表示第i个人	在 [r1 + 1 ~ p] 范围内取了多少个礼物
					//这里的p表示当前一共有多少个礼物
bool judge(int p)
{
	int x = r[1], y = p - r[1];
	Left[1] = x, Right[1] = 0;		//第一人,往左边取礼物
	for(int i = 2; i <= n; ++i)
	{
		if(i % 2 == 1)				//i为奇数,往右边取
		{
			Right[i] = min(y - Right[i - 1], r[i]);	//每个人最多取 r[i]个礼物	
			Left[i] = r[i] - Right[i];				//另一边取的礼物数量
		}else{						//i为偶数,往左边取
			Left[i] = min(x - Left[i - 1], r[i]);
			Right[i] = r[i] - Left[i];	
		}
	}
	return Left[n] == 0;
}

void solve()
{
	if(1 == n)
	{
		printf("%d\n", r[1]);
		return;
	}
	r[n + 1] = r[1];
	int L = 0, R = 0;	//L为 下限值,R 为上限值
	for(int i = 1; i <= n; ++i)
	{
		L = max(L, r[i] + r[i + 1]);
	}
	if(n % 2 == 1)	//为奇数时
	{
		for(int i = 1; i <= n; ++i)
		{
			R = max(R, 3 * r[i]);
		}
		while(L < R)
		{
			int M = (R + L) / 2;
			if(judge(M))
			{
				R = M;
			}else{
				L = M + 1;			
			}
		}
	}
	printf("%d\n", L);
}

int main()
{
	while(scanf("%d", &n) == 1 && n)
	{
		for(int i = 1; i <= n; ++i)
		{
			scanf("%d", &r[i]);
		}
		solve();
	}
	return 0;
}

/*
3
4
2
2
5
2
2
2
2
2
5
1
1
1
1
1
0
*/

/*
8
5
3
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值