zoj 2968 Difference Game [贪心+二分]
2012-08-11 14:06
225 查看
题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1967
题意:多case,有两个集合Ga和Gb,两个集合最开始都有S个元素,可以将Ga中的元素移动到Gb中,也可以将Gb中的元素移至Ga中,每次移动的花费为移动前两集合中元素个数之差的绝对值,在保证花费不超过最大花费C的情况下,使Ga中最小的元素-Gb中最大的元素的值最大,化成式子就是求Max{min{Ga}-max{Gb}}。
应该算是贪心吧,想了很久,推出了个结论过了,不过后面要用到二分,不过数据弱,不用二分也能过,正解应该要用二分查找的,不过写二分到是写苦了我,这次顺便总结一下二分。
思路:
1,只有最后min{Ga}>=max{Gb},最后输出的解才有可能为正数,而满足min{Ga}>=max{Gb}的情况就是将两个集合中的所有数重新分成两个集合,小的全部在Gb中,大的全部在Ga中。所以可以枚举所有情况,求出在满足不超过最大花费情况下的最大值即可,O(n);
2,枚举所有正数的情况如何计算最小花费:从Ga,Gb的原始状况到最终状况出发,如果最后分给Ga的元素k,最开始也在Ga中,则为了保证花费最小,在转移过程中元素k也不可能会到Gb中去,所以可以计算出Ga到Gb的元素个数ab和Gb到Ga的元素个数ba,可以证明,最开始一定是,Ga->Gb,Gb->Ga,Ga->Gb……或者Gb->Ga,Ga->Gb,Gb->Ga……,如果令x=min{ab,ba},y=fabs(ab-ba),最后的花费就是2*x+y*(y-1)。就在求ab和ba的时候要用到二分:对于此时枚举的Ga中最小元素p,求出原Ga集合中有多少个比最小元素p小的数,排序后用二分找出下标即可,而ba与ab有这种关系ba=2*n-i-(n-ab)
3,如果最后没有正解,则说明Ga中的最小元素小于Gb中的最大元素。对于Ga,Gb原集合,第一步一定是将Ga中的最小元素移至Gb中或将Gb中的最大元素移至Ga中。假设将Ga中的最小元素移至Gb中,如果这样的第一步操作满足最大花费,由上述1,2可知无正解,则移动后Ga中次小元素仍然小于Gb中的最大元素,接下来可以移动Gb中的最大元素,或者原Ga集合中的次小元素(现在G啊a中的最小元素),但可证明一定是将Gb中的元素移至Ga中。
如果是Ga->Gb,Gb->Ga,Ga->Gb花费为2;如果是Ga->Gb,Ga->Gb,Gb->Ga花费为6,而最后状态相同,所以不如选取前者,知道花费不满足条件就停止移动输出解。
代码如下:
ViewCode
下面我要说说二分,以下是celia01写过的各种错误二分版本
首先说一下,对于二分while()里面常常又两种写法while(l+1<r)和while(l<r),一般
while(l<r) 配 l=m+1和r=m-1
while(l+1<r) 配 l=m和r=m
这样才能有效跳出循环。题中要求a[]中小于temp的值有多少个,所以l=m+1无碍,但r=m-1就会损失解了。
其实对于上面的wa2很容易改变,笔者今天又重新交了一下就ac了,居然一下排到了榜首,两个二分就都凑齐了
题意:多case,有两个集合Ga和Gb,两个集合最开始都有S个元素,可以将Ga中的元素移动到Gb中,也可以将Gb中的元素移至Ga中,每次移动的花费为移动前两集合中元素个数之差的绝对值,在保证花费不超过最大花费C的情况下,使Ga中最小的元素-Gb中最大的元素的值最大,化成式子就是求Max{min{Ga}-max{Gb}}。
应该算是贪心吧,想了很久,推出了个结论过了,不过后面要用到二分,不过数据弱,不用二分也能过,正解应该要用二分查找的,不过写二分到是写苦了我,这次顺便总结一下二分。
思路:
1,只有最后min{Ga}>=max{Gb},最后输出的解才有可能为正数,而满足min{Ga}>=max{Gb}的情况就是将两个集合中的所有数重新分成两个集合,小的全部在Gb中,大的全部在Ga中。所以可以枚举所有情况,求出在满足不超过最大花费情况下的最大值即可,O(n);
2,枚举所有正数的情况如何计算最小花费:从Ga,Gb的原始状况到最终状况出发,如果最后分给Ga的元素k,最开始也在Ga中,则为了保证花费最小,在转移过程中元素k也不可能会到Gb中去,所以可以计算出Ga到Gb的元素个数ab和Gb到Ga的元素个数ba,可以证明,最开始一定是,Ga->Gb,Gb->Ga,Ga->Gb……或者Gb->Ga,Ga->Gb,Gb->Ga……,如果令x=min{ab,ba},y=fabs(ab-ba),最后的花费就是2*x+y*(y-1)。就在求ab和ba的时候要用到二分:对于此时枚举的Ga中最小元素p,求出原Ga集合中有多少个比最小元素p小的数,排序后用二分找出下标即可,而ba与ab有这种关系ba=2*n-i-(n-ab)
3,如果最后没有正解,则说明Ga中的最小元素小于Gb中的最大元素。对于Ga,Gb原集合,第一步一定是将Ga中的最小元素移至Gb中或将Gb中的最大元素移至Ga中。假设将Ga中的最小元素移至Gb中,如果这样的第一步操作满足最大花费,由上述1,2可知无正解,则移动后Ga中次小元素仍然小于Gb中的最大元素,接下来可以移动Gb中的最大元素,或者原Ga集合中的次小元素(现在G啊a中的最小元素),但可证明一定是将Gb中的元素移至Ga中。
如果是Ga->Gb,Gb->Ga,Ga->Gb花费为2;如果是Ga->Gb,Ga->Gb,Gb->Ga花费为6,而最后状态相同,所以不如选取前者,知道花费不满足条件就停止移动输出解。
代码如下:
ViewCode
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #definesee(x)cout<<#x<<":"<<x<<endl; usingnamespacestd; constintmaxn=20005; inta[maxn],b[maxn],c[maxn<<1]; boolcmp(intp,intq){ returnp>q; } intsearch2(inttemp,intn){ intl=-1,r=n,m; while(l+1<r){ m=(l+r)>>1; if(a[m]<temp){ l=m; } else{ r=m; } } returnr; } intmain(){ intt,n,C,i,j,k,l,x,y,ans,res,cost; intab,ba; scanf("%d",&t); while(t--){ scanf("%d%d",&n,&C); for(i=0;i<n;i++){ scanf("%d",&a[i]); c[i]=a[i]; } for(i=0;i<n;i++){ scanf("%d",&b[i]); c[i+n]=b[i]; } if(n==1){ printf("%d\n",a[0]-b[0]);continue; } sort(a,a+n);sort(b,b+n,cmp); sort(c,c+2*n); res=-50001; for(i=1;i<2*n;i++){ ab=search2(c[i],n); ba=2*n-i-(n-ab); x=min(ab,ba); y=(int)fabs(ab-ba); cost=2*x+y*(y-1); if(cost<=C){ res=max(res,c[i]-c[i-1]); } } if(res>0){ printf("%d\n",res); } else{ cost=0; i=j=0; k=0; while(cost<=C&&i<n&&j<n){ res=max(res,a[i]-b[j]); cost+=(int)fabs(i-j)*2; if(k&1){ i++; } else{ j++; } k++; } cost=0; i=j=0; k=0; while(cost<=C&&i<n&&j<n){ res=max(res,a[i]-b[j]); cost+=(int)fabs(i-j)*2; if(k&1){ j++; } else{ i++; } k++; } printf("%d\n",res); } } return0; }
下面我要说说二分,以下是celia01写过的各种错误二分版本
/*求a[]数组(其中元素是从小到大排序的)中值小于temp的元素有多少个,n为a[]数组大小*/ //wa1 intsearch2(inttemp,intn){ intl=0,r=n-1,m; while(l<r){ m=(l+r)>>1; if(a[m]<temp){ l=m+1; } else{ r=m-1;//损失解 } } returnl; } //wa2 intsearch2(inttemp,intn){ intl=0,r=n-1,m;//r=n-1一开始就损失解了 while(l<r){ m=(l+r)>>1; if(a[m]<temp){ l=m+1; } else{ r=m; } } returnl; } //wa3 intsearch2(inttemp,intn){ intl=0,r=n-1,m; while(l+1<r){//若l=3,r=4,a[3]<temp,进入死循环 m=(l+r)>>1; if(a[m]<temp){ l=m; } else{ r=m; } } returnr; } //accept: intsearch2(inttemp,intn){ intl=-1,r=n,m; while(l+1<r){ m=(l+r)>>1; if(a[m]<temp){ l=m; } else{ r=m; } } returnr; }
首先说一下,对于二分while()里面常常又两种写法while(l+1<r)和while(l<r),一般
while(l<r) 配 l=m+1和r=m-1
while(l+1<r) 配 l=m和r=m
这样才能有效跳出循环。题中要求a[]中小于temp的值有多少个,所以l=m+1无碍,但r=m-1就会损失解了。
其实对于上面的wa2很容易改变,笔者今天又重新交了一下就ac了,居然一下排到了榜首,两个二分就都凑齐了
//30ms intsearch2(inttemp,intn){ intl=0,r=n,m; while(l<r){ m=(l+r)>>1; if(a[m]<temp){ l=m+1; } else{ r=m; } } returnl;//这里l和r都无所谓 } //40ms intsearch2(inttemp,intn){ intl=-1,r=n,m; while(l+1<r){ m=(l+r)>>1; if(a[m]<temp){ l=m; } else{ r=m; } } returnr;//这里一定要是r }
相关文章推荐
- ZOJ 2968 Difference Game 【贪心 + 二分】
- zoj 3820 Building Fire Stations(贪心 + 二分)
- ZOJ 2002 Copying Books 二分 贪心
- zoj-3963 Heap Partition(贪心+二分+树状数组)
- (ZOJ) 3334 二分+贪心(二分double的写法)
- ZOJ 3963 Heap Partition( 并查集 + 贪心 +二分 )
- ZOJ 3778 Talented Chef (贪心+二分)
- 浙江14届省赛 F.Heap Partition(ZOJ 3963)[贪心][二分]
- zoj 2968 Difference Game (模拟)
- ZOJ 3090 Assemble(二分+贪心)
- ZOJ 3908 Number Game (贪心+二分+multiset)
- ZOJ 2002 Copying Books 二分 贪心
- ZOJ 3820 Building Fire Stations (二分+贪心) 2014 牡丹江现场赛B
- zoj 3963 Heap Partition(并查集,贪心,二分)
- Codeforces 609D 贪心+二分
- zoj 2570 Arena DP+搜索退化贪心
- ZOJ 2315 New Year Bonus Grant(贪心)
- codeforce 551C GukiZ hates Boxes(二分+贪心)
- 二分与贪心-Yogurt factory(算法基础 第9周)
- ZOJ 3612 Median(二分)