深搜的剪枝技巧(一)——樹的劃分(可行性剪枝、上下界剪枝)

深搜的剪枝技巧(一)——樹的劃分(可行性剪枝、上下界剪枝)

本系列的開篇之作,先介紹一下剪枝的概念

一、什麼是剪枝

  • 搜索的進程可以看成是從樹根出發,遍歷一顆倒置的樹——搜索樹的過程。剪枝就是通過某種判斷,避免一些不必要的遍歷過程

二、剪枝的原則

  • 正確性
  • 準確性
  • 高效性

三、 深度優先搜索的優化技巧

  • 優化搜索順序
  • 排除等效冗餘
  • 可行性剪枝
  • 最優性剪枝
  • 記憶化

四、樹的劃分(可行性剪枝、上下界剪枝)

  • 問題描述——將整數 n 劃分成 k 份,且每份不能爲空,問有多少種不同的分法?當 n = 7,k = 3 時,下面三種分法被認爲是相同的,1,1,5;1,5,1;5,1,1。

  • 輸入格式——輸入文件只有一行,爲兩個整數 n 和 k(\(6 < n \leq 200,2 \leq k \leq 6\))

  • 輸出格式——輸出文件只有一行,爲一個整數,即不同的分法數

  • 樣例輸入

    7 3
  • 樣例輸出

    4
  • 樣例解釋—— 4 種分法爲 1,1,5;1,2,4;1,3,3;2,2,3

  • 思路分析——將數字 n 劃分成 k 份,就是求 \(x_1 + x_2+...+x_k = n\) 方程的解,依次枚舉 \(x_1到x_k\) 的值,然後判斷

  • 剪枝分析

    • 由於分解數不考慮順序,因此設定分解數依次遞增,所以擴展結點時的“下屆”應是不小於前一個擴展結點的值,即 \(a[i-1] \leq a[i]\)
    • 假設我們將 n 已經分解成了 \(a[1] + a[2]+ ...+a[i-1]\),則 \(a[i]\) 的最大值爲將 i~k 這 k-i+1 份平均劃分,即設 \(m = n-(a[1]+a[2]+...+a[i-1])\),則 \(a[i]\leq m/(k-i+1)\),所以擴展結點的“上屆”是 \(\frac{m}{k-i+1}\)
  • 代碼:

    #include <iostream>
    
    using namespace std;
    
    int n,m,a[8],s= 0;
    
    void Solve(int k);
    
    int main()
    {
        cin>>n>>m;
        a[0] = 1;
        Solve(1);
        cout<<s<<endl;
        return 0;
    }
    void Solve(int k)
    {
        if(n == 0)
            return;
        if(k == m)
        {
            if(n >= a[k-1])
                s++;
            return;
        }
        for(int i = a[k-1];i<=n/(m-k+1);i++)
        {
            a[k] = i;
            n -= i;
            Solve(k+1);
            n += i;
        }
    }
posted @ 2018-08-13 16:17 Nikki_o3o 閱讀(...) 評論(...) 編輯 收藏
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章