标签:nbsp bre 优化 查看 神题 cap 硬币 enter target
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/32768 K (Java/Others)
Output
挺有意思的一道题,很常见(我为啥觉得常见嘞?)的一个定义。
给你三种硬币,面值分别为1,2,5。并给出它们各自的数量,请你求出最小的金额,无法用这些硬币的一个子集恰好表示。
这和FJOI的一道题很像,只是比那道题要弱很多,关于FJOI的那道题可以在我的Blog中搜索“福建神题”以查看详情。
那么,这么小的数据范围,做法显然多种多样,先讲蒟蒻(我)的做法好了。
显然这道题好多人给的标签都是生成函数,确实可以套一层生成函数的外壳就是了,但是其代码实现我觉得都用不着搬出生成函数来解释,而且无脑套生成函数会使得你的常数剧增(因为这题其实只是个01问题,好多人写模板都是直接求integer系数)。不过还是说一下。
这题按照题意,母函数就是$F(x)=(1+x+x^{2}+x^{3}+...+x^{k_{1}})(1+x^{2}+x^{4}+x^{6}+...+x^{2k_{2}})(1+x^{5}+x^{10}+x^{15}+...+x^{5k_{3}})=\sum_{i=0}^{k_{1}}{x^{i}}\sum_{i=0}^{k_{2}}{x^{2i}}\sum_{i=0}^{k_{3}}{x^{5i}}$,
然后就直接暴力做多项式乘法(貌似需要一点小小的优化,就是一开始边界不要给到8000什么的)就可以水过了。
然而我们不难看出这题只需要知道能否表示即可,并不需要计算具体的表示方案数,所以我们用一个bitset就可以表示所有系数是0还是其它了,这个不想手写可以用C++的STL,心疼Pascal童鞋一秒。
然后想起来,bitset的卷积,就是bitset的移位运算+或运算,所以还可以用移位运算优化一下,应该能快一些?(数据这么水,我也不知道能不能显现出来啊)
然后又想起来,bitset的卷积,写个优化算法就可以再除个log,那岂不是美滋滋?但是太难写了(偷个懒啦)。
所以贴上我的bitset暴力(母函数)的代码。
1 #include <bitset> 2 #include <cstdio> 3 4 int a, b, c; 5 6 std::bitset<8005> S; 7 8 signed main(void) 9 { 10 while (scanf("%d%d%d", &a, &b, &c), a || b || c) 11 { 12 S.reset(); 13 14 { 15 for (int i = 0; i <= a; ++i) 16 for (int j = 0; j <= b; ++j) 17 S.set(i + j * 2); 18 } 19 20 { 21 int lim = a + b * 2; 22 23 for (int i = lim; i >= 0; --i)if (S[i]) 24 for (int j = c; j >= 0; --j) 25 S.set(i + j * 5); 26 } 27 28 int ans = 1; 29 30 while (S[ans])++ans; 31 32 printf("%d\n", ans); 33 } 34 }
然后来说说机智的做法。
如果当前有一个集合,可以表示$[1,a]$之内的所有数字,那么考虑向其中加入一个元素$b$会怎样。
如果$b\leq a+1$,那么新集合可以表示$[1,a+b]$内的所有数字。
反之,$a+1$将无法被任何子集表示,所以最小的不可表示数字就是$a+1$。
根据这个性质,我们设置$ans=1$,并令其不断逼近答案,每次取出小于等于$ans$的所有数字之和设为$sum$,
如果$sum<ans$,那么ans就是无法被表达的(这是显然的吧,取出来的加一起都够不到$ans$,而没取出来的随便一个都比$ans$大)。
反之,我们就令$ans=sum+1$,继续该过程。
这个算法的复杂度是有保证的。考虑算法如果没有结束,说明$ans=sum+1$,那么下一次取出的数字中一定要有一个在$[lastAns,nowAns]$之间的数字,否则将在下一次取数后结束。而这区间内的一个数字是大于$lastSum$,也就是说取出的集合中的数字之和至少翻番,所以总的取数次数是$O(log(\sum_{a_{i}\in S}{a_{i}}))$的,其中$ai$是给定集合$S$的元素。而我们的取数操作显然是$O(1)$的,所以算法复杂度就是$O(log(\sum_{a_{i}\in S}{a_{i}}))$。
下面给出这个算法的代码实现,在HDU上0MS通过。
1 #include <cstdio> 2 3 int s[3] = {1, 2, 5}, c[3]; 4 5 signed main(void) 6 { 7 while (scanf("%d%d%d", c, c + 1, c + 2), c[0] || c[1] || c[2]) 8 { 9 int ans = 1; 10 11 while (true) 12 { 13 int sum = 0; 14 15 for (int i = 0; i < 3; ++i) 16 if (s[i] <= ans) 17 sum += s[i] * c[i]; 18 19 if (ans > sum) 20 break; 21 else 22 ans = sum + 1; 23 } 24 25 printf("%d\n", ans); 26 } 27 }
@Author: YouSiki
HDU 1085 Holding Bin-Laden Captive!
标签:nbsp bre 优化 查看 神题 cap 硬币 enter target
原文地址:http://www.cnblogs.com/yousiki/p/6422036.html