Day 3
贪心
典型例题:
- 区间调度问题
- 背包问题?引出性价比的思想
- 区间选点问题
总结:
1.贪心算法无回溯过程,后面做的每一步都是当前看似最佳的选择,这种选择依赖于已作出的决定,不依赖未作出的决定。
二分
1.二分搜索的一个注意点:如果向下取整写为middle = (left + right) / 2,那么会有2个风险。一个是left + right有可能爆本身的范围,另一个就是当和为负数的时候实现的是向零取整——也就是向上取整。为了规避这两个风险,可以采取这样的写法middle = left + (right - left) / 2
2.二分查找需要注意一些问题:终止条件(会不会死循环?)、middle的取值办法、开闭?
3.可进行二分答案的问题具有如下特征:1)答案容易检验,2)答案具有单调性。 有一些常见的题型:逼近答案、最大化最小、最小化最大、生成第k小值等。
这两块的ppt上有一些比较好的题目,留坑待补
Vj
C
一道具有平面背景的区间取点问题。
E
这题前前后后交了8发,前3发算法有毛病,修好了之后T飞。当时的算法是,make_heap后把前三个<int, map<int, int> t>拿出来,记录当前数字个数的map桶(t)都减一,再把这三个塞回去重新make_heap…用生成器开了一组自然数序列,果然直接t飞…后来改了算法,优化思想如下:从heap中开始从前往后扫(!!!在写报告的时候忽然意识到堆不是严格的递减的,是按照二叉树的形式递减的!那么扫的时候就有可能会漏!(不过仔细想想跳过去无非是提高了一些复杂度,应该不会wa啊…)),用int far记录数量与小堆顶元素数量相同的数字在该堆中的最远距离,然后三三合并,一口气合并完所有的个数,同时ans数组存入这么多个三元组。当然有可能会剩余一两个没有被合并的余项,不过这个时候它的数量影响已经微乎其微了,直接和剩余的堆中元素一起塞回去重新make_heap就可以了。修正后的算法跑自然数序列只用了毫秒级的时间就ok了,交上去后…却过了t的test10,wa在了test12…(coolwx当时也wa在了12,我过一阵空一些重构一下,再试试… … … …)
J
wa了好几发,因为在搜索的时候只搜索了存在性,没有考虑它的个数。事实上应该用upper_bound和lower_bound的组合拳来获得重复的个数。
K L
两个方程题,L用数学知识进行整理后也是一个方程类型的近似解求解问题。虽然它说有效数字在4位在6位…我都把eps开到了1e-9,基本上当left、middle、right的误差在1e-9附近的时候,4位6位都不在话下了。
Q
我开了两个数组,一个用来存原图,一个排好了序用来二分答案。前几次做直接从最小值最大值的区间中找答案,但是有可能这个答案不是木桶木板的长度,所以应该从顺序数组中二分找答案。当检验最小长度为k的时候,搜索一圈,把长度为k的点都要记录下来(这里不能只找一个,不然可以构造出反例,如:5 1 6 6 6 5 1 6 7 8 9 10)对于每一个点往后搜,遇到比它矮的就修一次墙壁,看最后要满足最小值是h的话至少需要多少次。如果该次数大于工具的使用次数,那么就不可以。
结果是wa了… … 可能有什么地方没注意到吧?
在写报告的时候想到,如果在距离该点小于等于 l - 1的位置粉刷一次?那么最小值可能就会比h要高?这样子的话最短高度就是木板中较高一些的最矮板?那么是否会产生遗漏呢?当然了,如果是上述这样的情况,left = middle, middle向上取整,会严格的搜索比目前高的长度,那么刚刚的那种情况也是会检验的。不用担心它是成立的、但被比他低的答案鸠占鹊巢。
当思考比他高的长度的检验时,不禁想到了有可能的wa点(关于粉刷次数的最小值确定?也就是说,从前往后遇到一个矮的就刷的办法是最优的吗?
仔细想想但没毛病啊…
夜深了…我想不出来了。
7月5号
E
之前的算法有问题啊…尽管前三个node 目前来看 是拥有最多个数数字的元素,但是我们的贪心策略应该是取出前三个最多的,然后cnt各自减一,塞回优先队列,再进行如上的操作。我之前的操作并不满足这样的流程,直接把目前最多的三个全部合并,这会导致解法不够优。
不过我这破办法居然过了42个test才wa掉…这数据真水啊
今天用priority_queue搞了一下就过去了,对于自然数序列也没有超时,开到满也就跑了0.2s。这样子看来,pq的实现是比普通的造堆要更优的,我之前t不是因为pq的算法不够优,而是因为当时之前不知道听了谁讲的,pq就是一个堆,所以我就用了stl库heap的make_heap等一系列堆的操作…
7月22号
D
刚开始想的是用数学办法作一个最优解求解,从而构造出答案要求的序列,但是发现还是挺麻烦的,复杂度会超过限制(N < 1e5)… 也没有想出来每头牛的 特征参数(用来刻画某头牛在这么多牛中的重要程度)。
后来注意到了性价比这一概念,用ratio = D / T来刻画某头牛在整个过程中的重要程度。在整个搬运牛的过程中,显然先把ration最高的牛先运走,再运较低的牛会比较好。证明办法其实可以用到之前提到的会爆t的办法,对于两头牛a, b交换成为b, a的代价是:2 * ( tb * da - ta * db),要比较它与0的关系,其实比较的就是他们的ratio(同时除以ta * tb即可),从而对ratio排序即可。
另外提一句,sort函数也可以直接比较积的形式来进行排序,时间复杂度保证是O(nlgn)。
当时没想到不过应该是很显然的事情…假期使人堕落(小声)…
F
股票购买问题。注意到这样一个事实,如果有这样三天的股票:5、6、7,那么购买6后再购买7与直接购买7获得的利润是一样的,我们称之为中转站思想,也可以想象成矢量的加法。因而根据这个办法,我们构造一个小顶堆,从前往后扫描,如果当前堆为空,数入堆;如果不空,并且满足堆顶元素小于当前数值,那么将堆顶弹出,push两次当前的元素数值,同时ans += x - pq.top();不然,数入堆。
因为在购买股票的过程中,我们真正关注的是差值,所以不断的加入差值更新ans即可。另外,加入的两个元素一个是该元素本身,另一个是购入原来的小顶堆堆顶价格、卖出当前价格后的等价价格,也就是之前提到的中转站思想。