码农的泪——二分答案总结
2016-05-15 14:20
477 查看
弄了一上午二分答案,终于弄明白了—— 一点点
第一点,在C++中,因为做除法是向下取整的,所以为了让每一次计算的mid在所选区间内,一定要将左区间闭上。
第二点,既然左边闭上了,那右边闭不闭呢?答案是不确定的。我自己喜欢左闭右开,和STL的习惯一样。我这个人很懒,但是这样也不容易出错。我做的一些题证明,左闭右开是可以解决这些问题的。下面分析都将建立与左闭右开的区间选择上讨论。
我们为什么要做二分优化呢?第一点,我们要的解答是有序的。第二点,当然是省时间了。《编程挑战》中说,进行100次二分验证,解的误差范围可以在10的-30次方左右。非常NICE。
那么是什么让我这么头痛呢?那就是整数的二分。
用两道例题来说一下。
第一道,poj3104晾衣服。
思路很简单,二分晾衣服的时间,每次检验,对于衣服i,它含有c[i]的水,那么让它晾干,如果它的水分<=day(即假设的总用时),那么就不需要熨烫。如果大于它,就需要满足以下关系式:
设它最少需要熨烫n次,那么有:
c[i]-k*n+n<=day
也就是求满足这个不等式的最小正整数。然而难点不在这里。
我们看主函数。如果我们的mid可以,就说明范围应该向左挪。那么细节来了:l,r,mid以及输出怎样来考虑?
对于一个区间[a,b),如果它的mid可以,那么接下来有两种情况,
1:mid就是最优解。
2:最优解在mid的左边。
所以我让r=mid,这样如果mid是最优解,输出r就是了。如果不是,那么就接着挪。
如果mid不可以呢?那么就让区间向右挪,由于区间左边是闭的,就不需要保留了,l=mid+1就可以了,而r还是前一次可行解
这样,最后会达到什么情况呢?
[a,a+1),根据前面的分析,a+1肯定是一个可行解。这时,区间的mid就是a,
如果a可行,r=a,此时区间里没有数了,a就是最优解,此时r=a,输出r就可以了。
如果a不可行,那么l=a+1,区间里没有数了,a+1就是可行解,而r一直就是可行解,输出r就可以了。
所以这是思考的精髓,就是让r始终是接近最优解的可行解。而对最后区间的分析,表明循环的要求是r-l>0
针对本题,它的坑点是k=1要单独判断,因为上面的不等式,解出n之后,分母是k-1!!!!!!!!!!!!!!!!!!
还有,要用long long ,因为上式中10^9的级别的数一乘会溢出int
这道题是最小化最大值,下面看一下最大化最小值。
poj3258奶牛跳石头兼NOIP2015Day2第一题,我居然一开始要用堆做,然而考完了也没反应过来是不对的。然后就跪了。
判断的地方,就要注意终点石头和最后剩下的那块之间的距离也要对就行了。
重点还是说二分。
这题我们怎样使用左闭右开区间呢?
总思路:用l控制接近最优解的可行解。
对于区间[l,r)
如果它的mid不可以,那么r=mid,那端不要了,不伤大雅。
如果它的mid可以,那么l=mid,也可以l=mid+1;
如果我选择了l=mid,那么最后是什么情况呢?
不太好想。假设最后是[l,l+2),它的mid一定是l+1。如果l+1可行,那么l=l+1,形成区间[l+1,l+2),l+2不行,那么l+1就是最优解。如果l+1不可行,那么r=l+1,l就是最优解。所以这种方法的循环判定就是r-l>1
如果我选择l=mid+1呢,假设最后是[l,l+1),l-1是那个最接近最优解的可行解,l是这个区间的mid,如果mid可行,那么l=l+1,区间里没有数了,现在的l就是最优解。说的有点迷糊。对于区间[5,6),4就是它最接近最优解的可行解,那么如果5可以,那么形成区间[6,6},这个区间的l-1,也就是5,就是我们要的答案。如果mid不行,r=5,形成区间[5,5),它的l-1,即4就是最优解。而我附的代码就是按第二种思路写的。
而第一题之所以没有r=mid-1的写法,因为右侧是开的,左+1,右-1,很容易出现[3,1)的情况,就难以控制了,我一测是WA,不好。
所以,这种二分答案的思路,大同小异,最主要的掌握核心:控制哪个是最接近最优解的可行解,然后就需要灵活处理,看最后的情况。这样,二分答案就没什么可怕的了,来几道,做几道。
祝大家学习进步,愿OI永葆青春。
第一点,在C++中,因为做除法是向下取整的,所以为了让每一次计算的mid在所选区间内,一定要将左区间闭上。
第二点,既然左边闭上了,那右边闭不闭呢?答案是不确定的。我自己喜欢左闭右开,和STL的习惯一样。我这个人很懒,但是这样也不容易出错。我做的一些题证明,左闭右开是可以解决这些问题的。下面分析都将建立与左闭右开的区间选择上讨论。
我们为什么要做二分优化呢?第一点,我们要的解答是有序的。第二点,当然是省时间了。《编程挑战》中说,进行100次二分验证,解的误差范围可以在10的-30次方左右。非常NICE。
那么是什么让我这么头痛呢?那就是整数的二分。
用两道例题来说一下。
第一道,poj3104晾衣服。
#include #include #include #include using namespace std; long long n,k,l,r; long long c[100100]; bool solve(int day) { long long count=0; for(int i=1;i<=n;i++) { if(c[i]<=day) continue; else { long long temp=(c[i]-day)/(k-1); if(temp*(k-1)0) { int mid=(l+r)/2; if(solve(mid)) r=mid; else l=mid+1; } printf("%lld\n",r); } return 0; }
思路很简单,二分晾衣服的时间,每次检验,对于衣服i,它含有c[i]的水,那么让它晾干,如果它的水分<=day(即假设的总用时),那么就不需要熨烫。如果大于它,就需要满足以下关系式:
设它最少需要熨烫n次,那么有:
c[i]-k*n+n<=day
也就是求满足这个不等式的最小正整数。然而难点不在这里。
我们看主函数。如果我们的mid可以,就说明范围应该向左挪。那么细节来了:l,r,mid以及输出怎样来考虑?
对于一个区间[a,b),如果它的mid可以,那么接下来有两种情况,
1:mid就是最优解。
2:最优解在mid的左边。
所以我让r=mid,这样如果mid是最优解,输出r就是了。如果不是,那么就接着挪。
如果mid不可以呢?那么就让区间向右挪,由于区间左边是闭的,就不需要保留了,l=mid+1就可以了,而r还是前一次可行解
这样,最后会达到什么情况呢?
[a,a+1),根据前面的分析,a+1肯定是一个可行解。这时,区间的mid就是a,
如果a可行,r=a,此时区间里没有数了,a就是最优解,此时r=a,输出r就可以了。
如果a不可行,那么l=a+1,区间里没有数了,a+1就是可行解,而r一直就是可行解,输出r就可以了。
所以这是思考的精髓,就是让r始终是接近最优解的可行解。而对最后区间的分析,表明循环的要求是r-l>0
针对本题,它的坑点是k=1要单独判断,因为上面的不等式,解出n之后,分母是k-1!!!!!!!!!!!!!!!!!!
还有,要用long long ,因为上式中10^9的级别的数一乘会溢出int
这道题是最小化最大值,下面看一下最大化最小值。
poj3258奶牛跳石头兼NOIP2015Day2第一题,我居然一开始要用堆做,然而考完了也没反应过来是不对的。然后就跪了。
#include #include #include using namespace std; int L,n,m; int stone[50010]; bool solve(int s) { int count=0,last=0,now=1; while(now<=n) { if(stone[now]-stone[last]>=s) { last=now; now++; } else { now++; count++; } } if(stone[n+1]-stone[last]>=s&&count<=m) return true; else return false; } int main() { #ifdef ONLINE_JUDGE #else freopen("in.txt","r",stdin); #endif scanf("%d %d %d",&L,&n,&m); stone[0]=0;stone[n+1]=L; for(int i=1;i<=n;i++) scanf("%d",&stone[i]); sort(stone+1,stone+n+1); int l=0,r=1000001000; while(r-l>0) { int mid=(l+r)/2; if(solve(mid)) l=mid+1; else r=mid; } printf("%d",l-1); }
判断的地方,就要注意终点石头和最后剩下的那块之间的距离也要对就行了。
重点还是说二分。
这题我们怎样使用左闭右开区间呢?
总思路:用l控制接近最优解的可行解。
对于区间[l,r)
如果它的mid不可以,那么r=mid,那端不要了,不伤大雅。
如果它的mid可以,那么l=mid,也可以l=mid+1;
如果我选择了l=mid,那么最后是什么情况呢?
不太好想。假设最后是[l,l+2),它的mid一定是l+1。如果l+1可行,那么l=l+1,形成区间[l+1,l+2),l+2不行,那么l+1就是最优解。如果l+1不可行,那么r=l+1,l就是最优解。所以这种方法的循环判定就是r-l>1
如果我选择l=mid+1呢,假设最后是[l,l+1),l-1是那个最接近最优解的可行解,l是这个区间的mid,如果mid可行,那么l=l+1,区间里没有数了,现在的l就是最优解。说的有点迷糊。对于区间[5,6),4就是它最接近最优解的可行解,那么如果5可以,那么形成区间[6,6},这个区间的l-1,也就是5,就是我们要的答案。如果mid不行,r=5,形成区间[5,5),它的l-1,即4就是最优解。而我附的代码就是按第二种思路写的。
而第一题之所以没有r=mid-1的写法,因为右侧是开的,左+1,右-1,很容易出现[3,1)的情况,就难以控制了,我一测是WA,不好。
所以,这种二分答案的思路,大同小异,最主要的掌握核心:控制哪个是最接近最优解的可行解,然后就需要灵活处理,看最后的情况。这样,二分答案就没什么可怕的了,来几道,做几道。
祝大家学习进步,愿OI永葆青春。
相关文章推荐
- 你离顶尖Java程序员,只差这11本书的距离 172 分享 分享到新浪微博 分享到QQ空间
- 每个程序员应该阅读的10本经典书籍
- 剑指offer 面试题39:判断平衡二叉树(LeetCode 110. Balanced Binary Tree) 题解
- 年薪10万和年薪100万的人,究竟差在哪里?
- 2016小米实习生面试心得
- Java多线程面试总结
- 总结Android面试问题
- 设计师和程序员-拓展知识关系网
- 职场7条黄金标准,你做到了几条?
- 程序员常用网站汇总
- Java面试题
- 面试:数组:Topk _1
- 程序员必须知道的10大基础实用算法及其讲解
- 微软面试100题-70
- 微软面试100题-69
- Java程序员的日常 —— static的用法讲解实践
- 剑指offer 面试题29:数组中出现次数超过一半的数字及其变形(腾讯2015秋招 编程题4)
- 机器学习常见面试题
- 软件众包 业余主义的复兴
- iOS面试题 汇总