二分与三分

耗尽温柔 提交于 2020-03-15 21:01:47
 设置越界下标确定是否存在
 1 //>=x
 2 while(l<r){
 3     int mid((l+r)>>1);
 4     if(a[mid]>=x) r=mid;
 5     else l=mid+1;
 6 }
 7 //<==x
 8 while(l<r)
 9 {
10     int mid((l+r+1)>>1);
11     if(a[mid]<=x) l=mid;
12     else r=mid-1;
13 }
14 //setprecision(3)
15 while(l+(1e-5)<r)
16 {
17     double mid=(l+r)/2;
18     if(calc(mid)) r=mid;
19     else l=mid;
20 }
21 FOR(i,0,100)
22 {
23     double mid((l+r)/2);
24     if(calc(mid)) r-mid;
25     else l=mid;
26 }

 

从学oi到现在,二分思想是逐渐发挥其威力的。其思想跟数学归纳法很像:

并不直接解决问题,而是将问题转化为几个相同的规模较小的问题。对于很简单的子问题(边界条件),直接给出答案。

对于高级数据结构,像线段树,平衡树都是基于二分思想

可以说,二分是算法大楼的重要根基之一。

用二分法配合基础算法打暴力,往往会有意想不到的收获。

 

对于有单调性的序列,二分能够在O(logn)的时间内进行查找。

 

具体应用:

1. 二分查找

  1. binary_search(a+n1,a+n2,k);True 找到 ,False 没找到
  2. Lower_bound(a+n1,a+n2,k[,cmp])

        可以排在值的后面

  1. upper_bound(a+n1,a+n2,k[,cmp])

        必须排在值的后面

2、二分答案

对于最大值最小化和最小值最大化的问题,可以二分答案,利用答案倒推。

经典例题

A. tree

给你一个无向带权连通图,每条边是黑色或白色。求一棵最小权的恰好有 need 条白色边的生成树

WQS二分

P2619 [国家集训队2]Tree I

https://www.luogu.org/problemnew/show/P2619

 1 const int MX=100010;
 2 struct edge{int u,v,w,col;}e[MX];
 3 int fa[50010];
 4 int n,m,k,l,r,mid,ans1,cc,t;
 5 void scan()
 6 {
 7     cin>>n>>m>>k;
 8     FOR(i,1,m) {cin>>e[i].u>>e[i].v>>e[i].w>>e[i].col;e[i].u++;e[i].v++;}
 9 }
10 bool cmp(edge aa,edge bb)
11 {
12     return aa.w==bb.w?aa.col<bb.col:aa.w<bb.w;
13 }
14 int findf(int x){return fa[fa[x]]==fa[x]?fa[x]:fa[x]=findf(fa[x]);}
15 bool mg(int x,int y)
16 {
17     int t1=findf(x);
18     int t2=findf(y);
19     if(t1==t2) return 0;
20     else {fa[t1]=t2;return 1;}
21 }
22 bool kruskal()
23 {
24     FOR(i,1,m) if(e[i].col==0) e[i].w+=mid;
25     FOR(i,1,n) fa[i]=i;
26     sort(e+1,e+m+1,cmp);
27     t=ans1=cc=0;
28     FOR(j,1,m)
29     {
30         if(mg(e[j].u,e[j].v))
31         {
32             ans1+=e[j].col^1;
33             cc+=e[j].w;
34         }
35         if(t==n-1) break;
36     }
37     return ans1<k;
38 }
39 void work()
40 {
41     l=-105;r=105;
42     while(l<r)
43     {
44         mid=l+r+1 >>1;
45         if(kruskal()) r=mid-1;
46         else l=mid;
47         FOR(i,1,m) if(e[i].col==0) e[i].w-=mid;
48     }
49     mid=l;kruskal();
50     cout<<cc-k*l;
51 }

 

 B. Chebnear

  最近,R终于获得了一片他梦寐以求的农场,但如此大的一片农场,想要做好防卫工作可不是一件容易的事。所以R购买了N个守卫,分别让他们站在一定的位置上(守卫不可移动,同一位置上至多有一个守卫)。但是,安排了所有的守卫之后,R才发现,守卫们彼此十分厌恶。经R研究,当某两个守卫距离≤K,他们就会发生争吵;但是,想要守卫们和解也是不难的——只需要R给出的平均工资能使两人满意,他们就会同意和解、成为朋友;当然,如果两个守卫有共同的朋友,他们也会和解成为朋友。R非常不想守卫们争吵,因此他想找出,在能使所有守卫闭嘴的前提下,平均工资的最小值是多少。他想到了一个绝妙的方法,但他忙着去Farm,没时间,所以这个任务就交给你了。

Sort边权值,从小到打拿 二分选边条数,每次判断不在同一联通快中中点的最小距离

 

4. 三分法

mid1=l+(r-l)/3;mid2=r-(r-l)/3;

对于单峰函数,最优点会与好点在坏点同侧

 

5. 分治解题

 Color zed的刷子宽度只有1,也就是说,一次只能将连续的一排或一列格子涂上颜色(长度任意)。zed想用最少的次数把栅栏全部涂上颜色(注意,一个格子不能重复涂色)

我理所当然的想用DP做,但是有特殊情况

解决办法:每次不断往上垒,碰到中断的地方就分治解决,分别枚举

(其实不需要离散化,>5000的直接丢掉即可)

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!