1. 程式人生 > >分治 二分答案 三分未完結

分治 二分答案 三分未完結

連通 優化 kruskal算法 fence tom ESS 二分 找到 直接

分治,字面上的解釋是"分而治之",就是把一個復雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合並。分治法是很多高效算法的基礎,如排序算法(快速排序,歸並排序),傅立葉變換(快速傅立葉變換)等等。

分治三步法:

1 劃分問題:把問題劃分成元素個數盡量相等的兩半;

2 遞歸求解:把兩半元素分別求解;

3 合並問題:把兩個已經求解的元素合並成一個;

二分的基本用途是在單調序列或單調函數中做查找操作,註意:二分一定是在一個單調有序的集合或函數中查找一個解;

整數定義域上的二分(模板):

int erfen(int l,int r){

  int l=1,r=n,ans;

  while(l<=r){

    int mid=(l+r)>>1;

    if(check(mid)) ans=mid,l=mid+1;

    else r=mid-1;

  }

  return ans;

}

註意,我們在二分實現中采用了右移運算>>1,而不是整數除法/2;前者是向下取整,後者是向零取整;

實數域上的二分(模板):

double erfen(double l,double r){

  double mid;

  while(fabs(l-r)>eps){

    mid=(l+r)/2.0;

    if(check(mid)) r=mid;

    else l=mid;

  }  

  return l;

}

fabs是求實數絕對值的函數,註意與整數絕對值函數abs的不同;

eps是要確定好的精度,一般題目要求保留k位小數,則取eps=1e-(k+2);

有時精度不容易確定或表示,就幹脆采用循環固定次數的二分方法,往往結果的精度更高(實數二分常常卡精度,爆精度);

for(int i=0;i<100;i++){

  double mid=(l+r)/2;

  if(check(mid)) r=mid;

  else l=mid;

}

二分答案:最小值最大(或最大值最小)的問題,常常選用二分法求解,同時配合貪心,DP等其他算法檢驗答案的合理性,將最優化問題轉化為判定性問題;

二分查找:用具有單調性的布爾表達式求解分界點,比如在有序數列中求數字x的排名;

例題:數列分段II(洛谷1182)

  對於給定的一個長度為N的正整數數列A,現要將其分成M(M≤N)段,並要求每段連續,且每段和的最大值最小。

  求最大值最小,典型的二分題;

例題:擴散(洛谷1661)

  一個點每過一個單位時間就會向四個方向擴散一個距離,兩個點a、b連通,記作e(a,b),當且僅當a、b的擴散區域有公共部分。連通塊的定義是塊內的任意兩個點u、v都必定存在路徑e(u,a0),e(a0,a1),…,e(ak,v)。給定平面上的n給點,問最早什麽時刻它們形成一個連通塊。

  方法一:二分時間t,運用並查集判斷連通塊;

  方法二:假設任意兩點之間有邊,相當於求所有點構成的最小生成樹中最長的一條邊。把兩點擴散連接的時長作為邊的權值,開一個結構體存邊,然後用kruskal算法求最小生成樹,找到其中最長的邊即可。

例題:Best Cow Fences(POJ2018)

例題:[Usaco2005 feb]憤怒的牛(BZOJ1734)

例題:Innovative Business

分治 二分答案 三分未完結