1. 程式人生 > 實用技巧 >區間dp-壓縮編碼

區間dp-壓縮編碼

題目大意:

  給定一段文字,已知單詞a1,a2, …,an出現的頻率分別t1,t2, …,tn。可以用01串給這些單詞編碼,即將每個單詞與一個01串對應,使得任何一個單詞的編碼(對應的01串)不是另一個單詞編碼的字首,這種編碼稱為字首碼。
  使用字首碼編碼一段文字是指將這段文字中的每個單詞依次對應到其編碼。一段文字經過字首編碼後的長度為:
  L=a1的編碼長度×t1+a2的編碼長度×t2+…+an的編碼長度×tn
  定義一個字首編碼為字典序編碼,指對於1 ≤i<nai的編碼(對應的01串)的字典序在ai+1

編碼之前,即a1,a2, …,an的編碼是按字典序升序排列的。
  例如,文字E A E C D E B C C E C B D B E中, 5個單詞A、B、C、D、E出現的頻率分別為1, 3, 4, 2, 5,則一種可行的編碼方案是A:000, B:001, C:01, D:10, E:11,對應的編碼後的01串為1100011011011001010111010011000111,對應的長度L為3×1+3×3+2×4+2×2+2×5=34。
  在這個例子中,如果使用哈夫曼(Huffman)編碼,對應的編碼方案是A:000, B:01, C:10, D:001, E:11,雖然最終文字編碼後的總長度只有33,但是這個編碼不滿足字典序編碼的性質,比如C的編碼的字典序不在D的編碼之前。
  在這個例子中,有些人可能會想的另一個字典序編碼是A:000, B:001, C:010, D:011, E:1,編碼後的文字長度為35。
  請找出一個字典序編碼,使得文字經過編碼後的長度L最小。在輸出時,你只需要輸出最小的長度L,而不需要輸出具體的方案。在上面的例子中,最小的長度L為34。

出題人很良心的說明了用哈夫曼編碼的正確性,所以就不能使用哈夫曼樹,所以要重新考慮這個問題,題目中的條件是字典序不能變,也就是左邊的一定小於右邊的字典序所以每次只能合併相鄰的兩個編碼才保證字典序遞增,現在的問題就變成了,如何通過合併相鄰兩個字元使得最終合併完的結果最小,那麼就變成了合併石子的板子題,就是區間dp了。

程式碼:

#include <iostream>
using namespace std;
const int N=1010;
int f[N][N];
int a[N],s[N];
int main(){
    int n;
    cin>>n;
    s[0]=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        s[i]=s[i-1]+a[i];
    }
    for(int len=2;len<=n;len++){
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            f[i][j]=0x3f3f3f3f;
            for(int k=i;k<=j;k++){
                f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
            }
        }
    }
    cout<<f[1][n]<<endl;
}