1. 程式人生 > 實用技巧 >多元Huffman編碼變形—回溯法

多元Huffman編碼變形—回溯法

一、問題描述

描述

在一個操場的四周擺放著n堆石子。現要將石子有次序地合併成一堆。規定在合併過程中最多可以有m(k)次選k堆石子合併成新的一堆,2≤k≤n,合併的費用為新的一堆的石子數。試設計一個演算法,計算出將n堆石子合併成一堆的最小總費用。 對於給定n堆石子,計算合併成一堆的最小總費用。

輸入

輸入資料的第1 行有1 個正整數n(n≤100),表示有n 堆石子。第2行有n個數,分別表示每堆石子的個數。第3 行有n-1 個數,分別表示m(k)(2≤k≤n)的值。

輸出Output

將計算出的最小總費用輸出。問題無解時輸出“No solution!”

Sample Input

7
45 13 12 16 9 5 22
3 3 0 2 1 0

Sample Output

136

問題分析

  • 首先用分支界限法(回溯法)找出每次合併石子堆數和可用次數v[i]
  • 然後對石子從小到大排序,每次取最小堆數合併石子(這樣保證越往後合併的堆數就越多)
  • 這樣就就可以保證最小輸出

程式碼

#include<iostream>
#include<algorithm>
using namespace std;

int n;
int p[201];
int m[101];
int v[101];
/**
分支界限法 找出每次合併石子堆數和可用次數
引數:第i次合併,還剩餘sum堆石子
*/
bool branch(int i,int sum){
    if(i==1){
        if(sum==1)
            return true;
        else
            return false;
    }
    for(int j=m[i];j>=0;j--){
        v[i] = j;
        if(sum-v[i]*(i-1)<=0){
            continue;
        }
        if(branch(i-1,sum-v[i]*(i-1))){
            return true;
        }
    }
    return false;
}

/**
把陣列P中的第n-1個數據,插入到i到n-2資料裡,
相當於把i到n從小到大排序
*/
void my_sorted(int i){
    for (int j = n-1; j >= i; --j) {
        if(p[j]<p[j-1]){
            swap(p[j],p[j-1]);
        }
        else
            break;
    }
}
int main(){
    cin>>n;
    for (int i = 0; i < n; ++i) {
        cin>>p[i];
    }
    for (int i = 2; i <= n; ++i) {
        cin>>m[i];
    }
    //判斷是否有解,並且把每次需要合併多少堆石子求出來
    if(!branch(n,n)){
        cout<<"No solution!"<<endl;
        return 0;
    }
    sort(p,p+n);

//        for(int i=0;i<=n;i++){
//            cout<<v[i]<<' ';
//        }
//        cout<<endl;

    int min = 0;
    int start = 0;
    int num = n;
    for (int i = 1; i <= num;i++) {
        for(int k = 0;k < v[i];k++){
            int min2 = 0;
            for (int j = 0; j < i; ++j) {
                min2 += p[start++];
            }
            p[n++] = min2;
            min += min2;
            my_sorted(start);
        }
    }
    cout<<min<<endl;
}