您的位置:首页 > 其它

UVALive 3177-贪心+二分

2015-11-09 11:43 295 查看
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
==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
)
maxx=tm[1]+tm
;

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
==0)
return 1;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: