Live Archive 3177 - Beijing Guards 二分+贪心

来源:互联网 发布:钢结构造价软件 编辑:程序博客网 时间:2024/06/05 07:32

题目链接

题意:给出n个人站一圈,第i个人需要a[i]种礼物,每种礼物都有无限多,要求相邻二人不能有重复种类的礼物。

问最少需要准备多少种礼物。(n<=100000,a[i]<=100000)


解法:首先想到了模拟,然后可以发现如果不站成圈,那么所求值是任意两个相邻的人所需要的礼物种类之和的最小值。


后来书上讲的我就没想到了,即使战成一个环,如果n为偶数,那么答案与站成一排相同,细想确实如此。


如果n为奇数,就要(着重考虑)想办法让最后一个人和第一个人选的礼物不重复。二分答案,对于每一个待测值x,

判断x是否成立的方法是:对礼物编号,第一个人从小选起,那么最后一个人需要尽量从大选起(尽量选右边的)。

所以倒数第二个人需要尽量选左边的...所以除了1以外,序号为奇数的人从右边选,偶数则从左边选。

最后判断第n个人是否选了第一个人选的部分。总之判断就是贪心。



即使n为偶数,虽然可以不用二分,但上面的思考方法同样成立,但是改为了序号为奇数的人从左边选,偶数则从右边选。


#include<cstdio>#include<string>#include<cstring>#include<iostream>#include<cmath>#include<algorithm>#include<vector>using namespace std;#define all(x) (x).begin(), (x).end()#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)typedef long long ll;typedef pair<int, int> pii;const int INF =0x3f3f3f3f;const int maxn=  100000  ;int n;int a[maxn+3],L[maxn+3],R[maxn+3];bool can(int x){   int LE=a[1],RI=x-a[1];   L[1]=a[1],R[1]=0;/*规定第一个人从左边选的,现在只考虑奇数个人的情况,为了能让最后一个人和第一个人选的东西尽量不重复   最后一个人应该从右边选,所以倒数第二个人尽量往左边选...所以除了1以外,序号为奇数的人从右边选,偶数则从左边选。   */   for(int i=2;i<=n;i++)   {       if(i%2){        R[i]=min(RI-R[i-1],a[i]);        L[i]=a[i]-R[i];       }       else{        L[i]=   min(LE-L[i-1],a[i]        );        R[i]=  a[i]-L[i];       }   }   return L[n]==0;//终极目的是判断1和n这两个人是否选了相同的东西。/* 判断的方法是根据把所需要的x个礼物编号,分成左右两部分,左边LE个,右边RI个。 其中LE=a[1],那么为了判断第n个人是否和第1个人有相同种类的礼物,首先第n个人应该尽量选右边的部分(贪心), 然后判断第n个人是否选了左边部分的某种礼物,即L[n]==0?*/}int solve(int le,int ri){    while(le<=ri)    {        int mid=(le+ri)>>1;        if(can(mid)) ri=mid-1;        else   le=mid+1;    }    return le;}int main(){    while(~scanf("%d",&n)&&n)    {        for1(i,n) scanf("%d",&a[i]);        if(n==1){//n==1时必须特判,否则会出错            printf("%d\n",a[1]);            continue;        }        a[n+1]=a[1];        int mini=0,maxi=0;//mini为二分下界,maxi为二分上界        for1(i,n)        {           maxi=max(maxi,3*a[i]);           mini=max(mini,a[i]+a[i+1]);        }        if(n%2) printf("%d\n", solve(mini,maxi)  );        else printf("%d\n",mini);/*注意n为偶数,而上述二分不再使用,因为        n为偶数,最后一个人应该尽量选右边的,倒数第二个人尽量选左边的...        所以应该是序号偶数的人右选,序号奇数的人左选和奇数个人的情况相反。经过测试是对的        */    }   return 0;}


0 0
原创粉丝点击