折半枚举,字面上就是只枚举一半,另一半要贪心。
多用于一些二枚举题,当n的大小很大时,直接枚举会超时,那可以只枚举一半(不是只计算一半,两边都要算,分开来算),另一半的值算出来后贪心的去取一个值是答案最优。
类似的题目有:疯狂的猜数字,礼物。
例一、疯狂的猜数字
在猜数字的基础上加大了数据范围,从四位数变成九位数,直接枚举肯定会超时。那就只枚举一半。这里的一半不是指数值的一半,而是数位的一半,即九位数分成四位数和五位数,分别对应枚举,前四位和前四位比较,后五位和后五位比较。
那怎么快速去寻找一个最优值呢?当枚举五位数时,对应的会求出一个数组,是后五位数为这个时有几位数位相同。把这个数组变成一个字符串,用map存一下每个字符串的出现次数,这个次数就是有多少个五位数会形成这种情况。
四位数同理,会有一个数组,然后与题目给的数组对应减去,得到的就是这个四位数后面加的五位数要形成的情况。然后就可以去找了。
说起来难以理解,带一组数据看看:
666666 666678 777777 999999
2 3 1 0
这个是读入数据,第二行是猜的数字和这几个数字相比有几个位置相同。
数位长为6,那我们就枚举三位数。
当用697去比后三位时,会得到1 1 1 1 这样的数组,变成字符串后是1111.
当用976去比后三位时,会得到1 1 1 1 这样的数组,变成字符串也是1111.
那此时1111就有两个值(实际可能不止两个)
当用679去枚举前三位时,会得到1 1 1 1 这样的数组,和原序列相减后是1 2 0 -1 ,出现了负数,可以直接省去。
当用667去比前三位时,会得到2 2 1 0这样的数组,相减后是0 1 0 0,变成字符串后是0100。
那就去找有多少个0100,如果多个直接输出”many answer”;若只有一个,把前三位数和对应的三位数合并成一个六位数,这样就是一个解了。
实现起来很简单,也就不放代码了。
例二、礼物
实际上就是把拔河的数据范围变大了,从20到30。
实际上用dfs也是可以过得,卡卡就行。
既然在折半枚举专题里,就是枚举一半。先算一半,然后把对应的值存起来;再算另一半,可以快速求出相应的答案。
对于这道题,用vector来存比较方便,下标是A选了几个,里面装的值是sumB-sumA。为什么装这个呢?因为求的是min(abs(sumA-sumB)),那当枚举前一半时算出来的sumA-sumB,去找一个和他求接近的sumB-sumA,两个相减,效果一样,而且方便查找。
代码写的太丑了,就不放了,再把思路理一遍
先读入两组数据
把后一半用二进制枚举计算出来,用vector存起来
把vector里的值排个序,用于lower_bound(开始忘了排序,错了两次)
把前一半用二进制枚举算出来,然后就在对应的vector中查找最优值,更新ans的值
输出ans
折半枚举是水分利器,也是很多题的正解(废话)。
从中可以学到一点:枚举不全部枚举,要有脑子的枚举,不能所有变量一起枚举,内在关系可以节省一些枚举量。