英雄会(csdn pongo)题解之罐子和硬币
2014-02-12 22:21
337 查看
这道题是春节前在家做的,当时题解写了一半,今天继续写的时候,发现自己都看不懂自己的代码了,又想了半天~
-------------------------------------------------------------------------------------------------------------------------------------
家里面真不是写代码的地,今天做这道题,中间出去n多次,到晚上9点才做出来。
输入n,k,c (0 < n <=1000000, 0 < c <= k <=1000000)。
输出,最小的p值。
例如n = 3, k = 6, c = 4。 你可以把每个罐子放入两个硬币,这样得到4次机会可以得到4个硬币,输出4。
1.需要理解的地方:
(1)题目中的“罐子被打乱顺序”、“存在一种你最初放硬币的方式”,意思是我们知道有几个罐子中放了x个硬币,有几个罐子中放了y个硬币....,但是我们不知道那个罐子中放有x个硬币,哪个罐子中放有y个硬币...;
(2)题目的意思是:k个硬币在n个罐子中有多种分布方式,如第i种分布方式的所有情况中通过至少p次机会才能拿到c个硬币,现在需要你设计一种硬币分布方式,使得在这种方式下的p值是所有分布方式中最小的,并求得p。
2.我的做法如下:
所求的p由两部分构成:拿到硬币的情况和罐子为空的情况,显然拿到硬币的情况为c。题目是所有情况下至少p次机会才能拿到c个硬币,即其中的空罐子肯定会被试探到。
分情况讨论:
(1)当k<=n时,那么有k个罐子均放一个硬币时p最小,此时p=n-k + c, 此时罐子为空的情况次数为n-k,因为如果存在某个罐子中多于一个硬币,那么空罐子的数量将多于n-k。
(2)当k>n时,设k=a*n+b(其中a=k/n,b=k%n):
[1]当c<=a*n时(此种情况包含了k能被n整除的情况),显然此时p=c,硬币的放法为每个罐子先放a个硬币,剩余的b个硬币随便放,拿的时候从每个罐子中拿a个,不会出现空罐子的情况。
[2]当c>a*n时,我们分下面四种情况:
假设我们用n个罐子中的x=n1+1个来放硬币,那么已经不可避免的空操作次数为n-x,并且硬币的数量k和x的关系显然就两种关系:
div= k/n1,ys=k%n1, ys<=n1-1
关系(1)div>=ys , 关系 (2) div<ys
关系(1)时最小p对应情况用下图表示,即n1个罐子放div个硬币,一个放ys个硬币:
因为这时如果c<=ys*x【情况1】那么,那么这时没有额外的空操作,p=n-x+c
即使c>ys*x【情况2】,这时也仅有一次空操作,这不能通过减小x时来避免这次空操作,因为减小x值会增加空罐子数量,所以p=n-x+c+1。
关系(2)时对应情况用下图表示
如果c<=div*x【情况3】那么,那么这时没有额外的空操作,显然上图是一种p最小的情况,p=n-x+c
如果c>div*x 【情况4】,那么在得到前div*x个硬币的时候不会有空操作,在从ys-div(绿色部分)取c-div*x个硬币的时候肯定会出现空操作,这时用x个罐子放硬币的空操作的次数至少是x-(ys-div),即将绿色部分一个一个添加到其他罐子里,而且这是在x个罐子放硬币的情况下的p最小值,如下:
证:如果不是用上图的方法,那么如上图所示增加一个罐子的硬币数量,肯定会减少另一个罐子中的数量,导致减少的罐子里面的硬币小于阀值,增加空操作的次数,得证。
那么 【情况4】下能否可以通过减小所用罐子数量x来减少空操作的次数呢?
如果在x减小x-(ys-div)-1个内,即x的最小值为(ys-div)+1,能将情况4转换为情况1,2,3那么就可以通过减小所用罐子数量x来减少空操作的次数。
3.代码如下:
4.时间复杂度为n-(k%(n-1)-k/(n-1))==O(n)
其实这道题有O(1)的解法,来自“绿色夹克衫”
c>m*n时,我们得从某些罐子里面能拿出>=m+1个硬币时,才能最终拿到c个硬币,也就是说向每个罐子得试探至少m+1次为止或者中间罐子为空为止,出现空操作的次数就是其中放了小于m+1个硬币的罐子,那么最多能在多少个罐子中放m+1个硬币呢,显然是最多k/(m+1)个罐子中能有m+1个硬币,现在空操作的次数是n-k/(m+1)最少,所以是p=c+n-k/(m+1)
-------------------------------------------------------------------------------------------------------------------------------------
家里面真不是写代码的地,今天做这道题,中间出去n多次,到晚上9点才做出来。
罐子和硬币
题目详情:
有n个罐子,有k个硬币,每个罐子可以容纳任意数量的硬币。罐子是不透明的,起初你可以随机把这k个硬币任意放在罐子里。然后罐子被打乱顺序,你从外表无法区别罐子。最后罐子被编上号1-n。你有p次机会,每次你可以选择某个罐子,如果该罐子里有硬币,则你可以得到1个(你不可以知道该罐子里有多少硬币),如果该罐子是空的,你得不到任何硬币。你最终要得到至少c枚硬币,我们的问题是给定n,k,c,求出最少的p,存在一种你最初放硬币的方式,无论罐子如何被打乱顺序,你都能p次机会内获得至少c个硬币。输入n,k,c (0 < n <=1000000, 0 < c <= k <=1000000)。
输出,最小的p值。
例如n = 3, k = 6, c = 4。 你可以把每个罐子放入两个硬币,这样得到4次机会可以得到4个硬币,输出4。
1.需要理解的地方:
(1)题目中的“罐子被打乱顺序”、“存在一种你最初放硬币的方式”,意思是我们知道有几个罐子中放了x个硬币,有几个罐子中放了y个硬币....,但是我们不知道那个罐子中放有x个硬币,哪个罐子中放有y个硬币...;
(2)题目的意思是:k个硬币在n个罐子中有多种分布方式,如第i种分布方式的所有情况中通过至少p次机会才能拿到c个硬币,现在需要你设计一种硬币分布方式,使得在这种方式下的p值是所有分布方式中最小的,并求得p。
2.我的做法如下:
所求的p由两部分构成:拿到硬币的情况和罐子为空的情况,显然拿到硬币的情况为c。题目是所有情况下至少p次机会才能拿到c个硬币,即其中的空罐子肯定会被试探到。
分情况讨论:
(1)当k<=n时,那么有k个罐子均放一个硬币时p最小,此时p=n-k + c, 此时罐子为空的情况次数为n-k,因为如果存在某个罐子中多于一个硬币,那么空罐子的数量将多于n-k。
(2)当k>n时,设k=a*n+b(其中a=k/n,b=k%n):
[1]当c<=a*n时(此种情况包含了k能被n整除的情况),显然此时p=c,硬币的放法为每个罐子先放a个硬币,剩余的b个硬币随便放,拿的时候从每个罐子中拿a个,不会出现空罐子的情况。
[2]当c>a*n时,我们分下面四种情况:
假设我们用n个罐子中的x=n1+1个来放硬币,那么已经不可避免的空操作次数为n-x,并且硬币的数量k和x的关系显然就两种关系:
div= k/n1,ys=k%n1, ys<=n1-1
关系(1)div>=ys , 关系 (2) div<ys
关系(1)时最小p对应情况用下图表示,即n1个罐子放div个硬币,一个放ys个硬币:
因为这时如果c<=ys*x【情况1】那么,那么这时没有额外的空操作,p=n-x+c
即使c>ys*x【情况2】,这时也仅有一次空操作,这不能通过减小x时来避免这次空操作,因为减小x值会增加空罐子数量,所以p=n-x+c+1。
关系(2)时对应情况用下图表示
如果c<=div*x【情况3】那么,那么这时没有额外的空操作,显然上图是一种p最小的情况,p=n-x+c
如果c>div*x 【情况4】,那么在得到前div*x个硬币的时候不会有空操作,在从ys-div(绿色部分)取c-div*x个硬币的时候肯定会出现空操作,这时用x个罐子放硬币的空操作的次数至少是x-(ys-div),即将绿色部分一个一个添加到其他罐子里,而且这是在x个罐子放硬币的情况下的p最小值,如下:
证:如果不是用上图的方法,那么如上图所示增加一个罐子的硬币数量,肯定会减少另一个罐子中的数量,导致减少的罐子里面的硬币小于阀值,增加空操作的次数,得证。
那么 【情况4】下能否可以通过减小所用罐子数量x来减少空操作的次数呢?
如果在x减小x-(ys-div)-1个内,即x的最小值为(ys-div)+1,能将情况4转换为情况1,2,3那么就可以通过减小所用罐子数量x来减少空操作的次数。
3.代码如下:
#include <stdio.h> #include <iostream> #include <string> using namespace std; class Test { public: static int pvalue (int n,int k,int c){ if(k<=n) return n-k+c; int kn=k-k%n;//kn=k/n*n if(kn>=c)return c; int n1=n-1;//初始值 int x=n1+1;//n int ys=k%n1; int div=k/n1;//x if(div>=ys) if(c<=ys*x)//情况1 return c; else //情况2 return 1+c; else if(c<=div*x)//情况3 return c; else{//情况4 int xMin=ys-div; while(x>xMin)//减少x-(ys-div)-1个罐子内 if(div>=ys) if(c<=ys*x)//转换为情况1 return n-x+c; else //转换为情况2 return n-x+1+c; else if(c<=div*x)//转换为情况3 return n-x+c; else{ --x; --n1; ys=k%n1; div=k/n1; }//else return n-xMin+c;//不能转换为其他三种情况 }//else } }; //start 提示:自动阅卷起始唯一标识,请勿删除或增加。 int main(){ cout<<Test::pvalue(3,6,4)<<endl; cout<<Test::pvalue(3,4,4)<<endl; cout<<Test::pvalue(4,6,5)<<endl; } //end //提示:自动阅卷结束唯一标识,请勿删除或增加。
4.时间复杂度为n-(k%(n-1)-k/(n-1))==O(n)
其实这道题有O(1)的解法,来自“绿色夹克衫”
int m = k / n; return c <= m * n ? c : c + n - k / (m + 1);其中c<=m*n时,显然是p=c;
c>m*n时,我们得从某些罐子里面能拿出>=m+1个硬币时,才能最终拿到c个硬币,也就是说向每个罐子得试探至少m+1次为止或者中间罐子为空为止,出现空操作的次数就是其中放了小于m+1个硬币的罐子,那么最多能在多少个罐子中放m+1个硬币呢,显然是最多k/(m+1)个罐子中能有m+1个硬币,现在空操作的次数是n-k/(m+1)最少,所以是p=c+n-k/(m+1)
相关文章推荐
- 英雄会(csdn pongo)题解之朋友的礼物
- 英雄会(csdn pongo)题解之字符串转换成整数
- 英雄会(csdn pongo)题解之坐标和数字
- 英雄会(csdn pongo)题解之求导数
- 英雄会(csdn pongo)题解之坐标和数字(Java版)
- 英雄会(csdn pongo)题解之报数游戏3
- 英雄会(csdn pongo)题解之平衡二叉树
- 英雄会(csdn pongo)题解之平衡二叉树——C++源代码
- CSDN2007年英雄会札记 - 聚会众生相(靓女篇)
- 英雄会(csdn pongo)题解之半质数的个数--2·14情人&元宵节专题
- 51Nod 1246 罐子和硬币
- 英雄会(csdn 高校俱乐部)题解之友好数(1)
- 1246 罐子和硬币 模拟题,感觉只能模拟
- 编程挑战之罐子和硬币思路
- 51nod 1246 罐子和硬币
- 罐子与硬币--【英雄会】
- 51NOD1246 罐子和硬币
- 【51Nod 1246】罐子和硬币
- 51nod-1246:罐子和硬币
- 51nod 1246 罐子和硬币