UVALive 3177-贪心+二分

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=18523

题意:n个人围成一圈,第i个人需要tm[i]个不同的礼物,相邻的两个人 不能有同样种类的礼物,第n个人和第一个人是相邻的

问最少要多少种礼物 可以满足要求  (每种礼物数量无穷多,可以重复使用)


对于n为偶数的情况 只需要 求一个最大的max(tm[i]+tm[i+1])  显然 奇数的人选前面的部分,偶数的人选后面的部分,就永远不会有重复了

而n为奇数的情况,如果也用上面的方法,会出现一个情况,,第1个人和第n个人都是奇数,他们可能选到重复的,那么我们需要让第n-1个人 (编号是偶数) 选的礼物种类尽量和第一个人相同,因为他们不是相邻,所以可以尽量相同。。那么第n个人就有更多的礼物可以选择 使得既不和第n-1个人冲突  又不和第1个人冲突了。。。

而这样选择的方案 显然是 第一个人 选 1到 tm[1],然后后面的  编号为偶数的人尽量选靠左边的,编号为奇数选尽量靠右边的,    才会使得 第n-1个人 选的礼物ID尽量靠近左边(也就是尽量和第1个人相同)那么第n个人的选择方案就尽可能地多了。。。。  二分这个方案数。。。逼近答案


判断函数:

为了方便起见,在判断X个礼物是否可行时,我们定义 编号为1到 tm[1] 的礼物为  左半部分 剩下的为右半部分

即ll_has=tm[1],rr_has=X-tm[1];

如果编号为偶数 (尽量拿左边的)

ll[i]= min(tm[i],ll_has-ll[i-1])  //前一个人选了ll[i-1]个,那么剩余ll_has-ll[i-1]个可选,尽量选够

rr[i]=tm[i]-ll[i]     //如果不能全部在“左边”选 ,则只能在“右边”选够剩下的

如果编号为奇数 (尽量拿右边的)

rr[i]=min(tm[i],rr_has-rr[i-1])   //前一个人选了rr[i-1]个,那么剩余rr_has-rr[i-1]个可选,尽量选够

ll[i]=tm[i]-rr[i];  //同理 选不够在左边选到满;


最后我们看第n个人  

由于第一个人选的礼物 是1-tm[i] ,这部分全是左边的、、、也就是第n个人 完全不能选左边的

如果ll[n]==0,表示当前礼物数X 能使得 第n个人全部选到右边的,,也就是和第一个人不冲突。

由于X的下界是 max(tm[i]+tm[i+1])  所以 相邻的两个人必然也是能满足 不冲突的条件的!

此方案合法!


其实主要就是判断第n个人和第1个人是否冲突

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
int min(int a,int b)
{return a<b?a:b;}
int tm[100005];
int ll[100005];
int rr[100005];
int n;
int main()
{
	int i,j;
	int ok(int);
	while(cin>>n&&n)
	{
		for (i=1;i<=n;i++)
		{
			scanf("%d",&tm[i]);
		}
		if (n==1)
		{
			printf("%d\n",tm[1]);continue;  //spj
		}
		int maxx=0; 
		for (i=1;i<n;i++)
		{
			if (maxx<tm[i]+tm[i+1])
				maxx=tm[i]+tm[i+1];
		}
			if (maxx<tm[1]+tm[n])
	 		maxx=tm[1]+tm[n];
	
		int l=maxx;
		int r=100000*3;
		if (n%2)  //如果偶数就直接输出maxx。不用二分找答案
		while(l<r)
		{
			if (r-l==1)
			{
				if (!ok(l))
				l=r;
				break;
			}
			int mid=(l+r)/2;
			if (ok(mid))
				r=mid;
			else
				l=mid+1;

		}
		printf("%d\n",l);  

	}
	
	return 0;
}
 
int ok(int x)
{
	int i; 
	ll[1]=tm[1];
		rr[1]=0;
		int ll_has=ll[1];
		int rr_has=x-ll[1];
		for (i=2;i<=n;i++)
		{
			if (i%2==0)//偶数尽量靠左选,使得第n-1位(偶数)的礼物id尽量和第一位相同,使得第n位能尽可能有更多的礼物可选
			{
				ll[i]=min(tm[i],ll_has-ll[i-1]); //如果不能拿够则尽量拿多点
				rr[i]=tm[i]-ll[i];			//然后再从右边拿够
			}
			else
			{
				rr[i]=min(tm[i],rr_has-rr[i-1]);
				ll[i]=tm[i]-rr[i];
			}
		}

		if (ll[n]==0)  
		return 1;
		return 0;
} 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值