回溯算法

回溯法

回溯法的算法框架

1. 綜述

  • 從問(wèn)題的 解空間樹(shù) 中,按照 深度優(yōu)先 的策略医增,從根節(jié)點(diǎn)出發(fā)搜索解空間樹(shù)。
  • 回溯法求所有解時(shí)老虫,最終需要回溯到根叶骨,并且所有節(jié)點(diǎn)的字?jǐn)?shù)都已被搜索遍才結(jié)束。求一個(gè)解時(shí)祈匙,遇到一個(gè)解便可以結(jié)束忽刽。
  • 回溯法適用于組合數(shù)較大的問(wèn)題。

2. 解空間

  • 解空間應(yīng)該至少包含問(wèn)題的一個(gè)解
  • 解空間應(yīng)該很好地組織起來(lái)夺欲,通常組織成樹(shù)或者圖

3. 基本思想

  • 活結(jié)點(diǎn)跪帝、擴(kuò)展結(jié)點(diǎn)、死結(jié)點(diǎn)
  • 約束函數(shù)剪去不滿足約束條件的子樹(shù)
  • 限界函數(shù)剪去得不到最優(yōu)解的子樹(shù)
  • 基本步驟
  1. 針對(duì)所給的問(wèn)題些阅,定義問(wèn)題的解空間伞剑;
  2. 確定易于搜索的解空間;
  3. 以深度優(yōu)先方式搜索解空間市埋,并在搜索的過(guò)程中用剪枝函數(shù)避免無(wú)效搜索黎泣。

4. 遞歸回溯

/*
  t:遞歸深度
  n:最大深度
  f(n,t):當(dāng)前擴(kuò)展結(jié)點(diǎn)處未搜索過(guò)的子樹(shù)的起始編碼
  g(n,t):當(dāng)前擴(kuò)展結(jié)點(diǎn)處未搜索過(guò)的子樹(shù)的終止編碼
  Constraint(t):約束函數(shù)
  Bound(t):限界函數(shù)


  自頂向下,
  對(duì)每個(gè)結(jié)點(diǎn)的分支進(jìn)行遞歸調(diào)用  for(int i=f(n,t);i<=g(n,t);i++)
*/
void Backtrack(int t)
{
  if(t > n) Output(x);  //是否遞歸結(jié)束
  else
  {
    for(int i=f(n,t);i<=g(n,t);i++)  //保證所有子樹(shù)要不被遍歷缤谎,要么被剪枝
    {
      t=i;
      if(Constraint(t)&&Bound(t)) Backtrack(i+1);
    }
  }
}

5. 迭代回溯

/*
自頂向下抒倚,
對(duì)每個(gè)結(jié)點(diǎn)的分支進(jìn)行迭代  for(int i=f(n,t);i<=g(n,t);i++)
*/
void IterativeBacktrack(void)
{
  int t=1;
  while(t > 0)
  {
    if(f(n,t) <= g(n,t))
    {
      for(int i=f(n,t);i<=g(n,t);i++)
      {
        t=i;
        if(Constraint(t)&&Bound(t))
        {
          if(Solution(t)) Output(x); //Solution(t)用于判斷問(wèn)題是都得以解決
          else t++;
        }
        else t--;
      }
    }
  }
}

6. 子集樹(shù)

從結(jié)合S中尋找滿足某種性質(zhì)的子集時(shí),相應(yīng)的解空間樹(shù)稱為子集樹(shù)坷澡,如0-1背包問(wèn)題托呕。
子集樹(shù)一般為完全二叉樹(shù),也就是由“要、不要项郊、要馅扣、不要等”形成。

void Backtrack(int t)
{
  if(t > n) Output(x);  //是否遞歸結(jié)束
  else
  {
    for(int i=0;i<=1;i++)  //保證所有子樹(shù)要不被遍歷呆抑,要么被剪枝
    {
      t=i;
      if(Constraint(t)&&Bound(t)) Backtrack(i+1);
    }
  }
}

7. 排列樹(shù)

確定n個(gè)元素滿足某種性質(zhì)的排列時(shí)岂嗓,相應(yīng)的解空間樹(shù)稱為排列樹(shù)。排列樹(shù)通常有n!個(gè)葉結(jié)點(diǎn)鹊碍。例如旅行售貨員問(wèn)題厌殉。

void Backtrack(int t)
{
  if(t > n) Output(x);
  else
  {
    for(int i=t;i<=n;i++)
    {
      Swap(x[t],x[i]);
      if(Constraint(t)&&Bound(t)) Backtrack(i+1);
      Swap(x[i],x[t]);
    }
  }
}

貨箱裝載

1. 問(wèn)題描述

兩艘船,n個(gè)貨箱侈咕。第一艘載重量c1公罕,第二艘載重量c2。wi是貨箱i的重量耀销,∑wi<=c1+c2楼眷。確定一種方法把n個(gè)貨箱全部裝上船。
∑wi<=c1=c2熊尉,原問(wèn)題等價(jià)于子集之和問(wèn)題罐柳;c1=c2,原問(wèn)題等價(jià)于分割問(wèn)題狰住。這兩個(gè)問(wèn)題都是NP-復(fù)雜問(wèn)題张吉。
解決辦法 :盡可能將第一艘船轉(zhuǎn)載到它的轉(zhuǎn)載極限,在將剩余的裝載到第二艘催植。
為了將第一艘船盡可能裝滿肮蛹,需要一個(gè)貨箱的子集,使得他們的總重量接近于c1创南。這個(gè)問(wèn)題可以通過(guò)0/1背包問(wèn)題來(lái)解決伦忠。

2. 遞歸回溯算法

屬于上述的子集樹(shù)解決辦法。

/*
貨箱重量weight[1:numberOfContainers]
rLoad(1):返回<=capacity的最大子集之和
*/
void rLoad(int currentLevel)
{
  //從currentLevel處的節(jié)點(diǎn)開(kāi)始搜索
  if(currentLevel > numberOfContainers)
  {
    //到達(dá)一個(gè)葉節(jié)點(diǎn)處
    if(weightOfCurrentLoading > maxWeightSoFar)
    maxWeightSoFar = weightOfCurrentLoading;
    return;
  }
  //還未到達(dá)葉節(jié)點(diǎn)稿辙,檢查子樹(shù)
  if(weightOfCurrentLoading + weight[currentLevel] <= capacity)
  {
    //搜索左子樹(shù)昆码,即x[currentLevel]=1
    weightOfCurrentLoading += weight[currentLevel];
    rLoad(currentLevel + 1);
    weightOfCurrentLoading -= weight[currentLevel];
  }
  //搜索左子樹(shù),即x[currentLevel]=0邻储,既然為0那么可以無(wú)需檢查而得以繼續(xù)
  rLoad(currentLevel + 1);
}

3. 尋找最優(yōu)子集

增加代碼來(lái)尋找到當(dāng)前的最優(yōu)子集未桥,為此使用一組數(shù)組bestLoadingSoFar,當(dāng)且僅當(dāng)bestLoadingSoFar[i]=1時(shí),貨箱i屬于最優(yōu)子集芥备。

/*
報(bào)告最有裝載的預(yù)處理程序
*/
int maxLoading(int *theWeight, int theNumberOfContainers, int theCapacity, int *bestLoading)
{
  /*
  數(shù)組theWeight[1:theNumberOfContainers]是貨箱重量
  theCapacity是船的載貨量
  數(shù)組bestLoading[1:theNumberOfContainers]是解
  返回最大載重量
  */
  //初始化全局變量
  numberOfContainers = theNumberOfContainers;
  weight =theWeight;
  capacity = theCapacity;
  weightOfCurrentLoading = 0;
  maxWeightSoFar = 0;
  currentLoading = new int [numberOfContainers+1];
  bestLoadingSoFar = bestLoading;

  //remainingWeight的初始值是所有貨箱重量之和
  for(int i=1;i<=numberOfContainers;i++)
  {
    remainingWeight += weight[i];
  }

  //計(jì)算最優(yōu)裝載的重量
  rLoad(1);
  return maxWeightSoFar;
}
/*
報(bào)告最優(yōu)裝載的回溯算法
*/
void rLoad(int currentLevel)
{
  //從currentLevel處開(kāi)始搜索
  if(currentLevel > numberOfContainers)
  {
    //到達(dá)了一個(gè)葉節(jié)點(diǎn)冬耿,存儲(chǔ)一個(gè)更優(yōu)解
    for(int j=1; j <= numberOfContainers; j++)
      bestLoadingSoFar[j] = currentLoading[j];
    maxWeightSoFar = weightOfCurrentLoading;
    return;
  }

  //沒(méi)有到達(dá)一個(gè)葉節(jié)點(diǎn),檢查子樹(shù)
  remainingWeight -= weight[currentLevel];
  if(weightOfCurrentLoading + weight[currentLevel] <= capacity)
  {
    //搜索左子樹(shù)
    currentLoading[currentLevel] = 1;
    weightOfCurrentLoading += weight[currentLevel];
    rLoad(currentLevel + 1);
    weightOfCurrentLoading -= weight[currentLevel];
  }

  if(weightOfCurrentLoading + remainingWeight > maxWeightSoFar)
  {
    //搜索右子樹(shù)
    rLoad(currentLevel + 1);
  }

  remainingWeight += weight[currentLevel];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末萌壳,一起剝皮案震驚了整個(gè)濱河市亦镶,隨后出現(xiàn)的幾起案子日月,更是在濱河造成了極大的恐慌,老刑警劉巖缤骨,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爱咬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绊起,警方通過(guò)查閱死者的電腦和手機(jī)精拟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)虱歪,“玉大人蜂绎,你說(shuō)我怎么就攤上這事∷癖桑” “怎么了师枣?”我有些...
    開(kāi)封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)萧落。 經(jīng)常有香客問(wèn)我践美,道長(zhǎng),這世上最難降的妖魔是什么找岖? 我笑而不...
    開(kāi)封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任陨倡,我火速辦了婚禮,結(jié)果婚禮上许布,老公的妹妹穿的比我還像新娘兴革。我一直安慰自己,他們只是感情好爹脾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布帖旨。 她就那樣靜靜地躺著箕昭,像睡著了一般灵妨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上落竹,一...
    開(kāi)封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天泌霍,我揣著相機(jī)與錄音,去河邊找鬼述召。 笑死朱转,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的积暖。 我是一名探鬼主播藤为,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼夺刑!你這毒婦竟也來(lái)了缅疟?” 一聲冷哼從身側(cè)響起分别,我...
    開(kāi)封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎存淫,沒(méi)想到半個(gè)月后耘斩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桅咆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年括授,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岩饼。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荚虚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出忌愚,到底是詐尸還是另有隱情曲管,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布硕糊,位于F島的核電站院水,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏简十。R本人自食惡果不足惜檬某,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望螟蝙。 院中可真熱鬧恢恼,春花似錦、人聲如沸胰默。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)牵署。三九已至漏隐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奴迅,已是汗流浹背青责。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留取具,地道東北人脖隶。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像暇检,于是被迫代替她去往敵國(guó)和親产阱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 目錄 1.回溯算法1.1 回溯算法簡(jiǎn)介1.2 一般回溯方法 2.收費(fèi)公路重建問(wèn)題(通過(guò)考慮最大值策略块仆,對(duì)可能性空間...
    王偵閱讀 12,550評(píng)論 0 3
  • 1.基本概念 回溯算法實(shí)際上一個(gè)類似枚舉的搜索嘗試過(guò)程构蹬,主要是在搜索嘗試過(guò)程中尋找問(wèn)題的解酿矢,當(dāng)發(fā)現(xiàn)已不滿足求解條件...
    RavenX閱讀 8,265評(píng)論 1 2
  • 引言:這道題目老師強(qiáng)調(diào)了肯定要考,所以只有硬著頭皮將其復(fù)習(xí)了;下面是自己學(xué)習(xí)回溯算法的學(xué)習(xí)怎燥,僅供參考瘫筐;一:基本概念...
    cp_insist閱讀 8,586評(píng)論 4 3
  • 貪心算法 先來(lái)比較一下貪心算法和動(dòng)態(tài)規(guī)劃 貪心算法是指在對(duì)問(wèn)題求解時(shí),總是做出在當(dāng)前看來(lái)是最好的選擇铐姚,不考慮整體策肝,...
    Moonsmile閱讀 2,780評(píng)論 0 1
  • 回溯算法 主要思想 回溯算法的基本思想是:從一條路往前走,能進(jìn)則進(jìn)隐绵,不能進(jìn)則退回來(lái)之众,換一條路再試。八皇后問(wèn)題就是回...
    愛(ài)撒謊的男孩閱讀 1,101評(píng)論 0 3