CCF CSP 壓縮編碼
試題編號: 201612-4
試題名稱: 壓縮編碼
時間限制: 3.0s
記憶體限制: 256.0MB
問題描述:
問題描述
給定一段文字,已知單詞a1, a2, …, an出現的頻率分別t1, t2, …, tn。可以用01串給這些單詞編碼,即將每個單詞與一個01串對應,使得任何一個單詞的編碼(對應的01串)不是另一個單詞編碼的字首,這種編碼稱為字首碼。
使用字首碼編碼一段文字是指將這段文字中的每個單詞依次對應到其編碼。一段文字經過字首編碼後的長度為:
L=a1的編碼長度×t1+a2的編碼長度×t2+…+ an的編碼長度×tn。
定義一個字首編碼為字典序編碼,指對於1 ≤ i < n,ai的編碼(對應的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。
輸入格式
輸入的第一行包含一個整數n,表示單詞的數量。
第二行包含n個整數,用空格分隔,分別表示a1, a2, …, an出現的頻率,即t1, t2, …, tn。請注意a1, a2, …, an具體是什麼單詞並不影響本題的解,所以沒有輸入a1, a2, …, an。
輸出格式
輸出一個整數,表示文字經過編碼後的長度L的最小值。
樣例輸入
5
1 3 4 2 5
樣例輸出
34
樣例說明
這個樣例就是問題描述中的例子。如果你得到了35,說明你算得有問題,請自行檢查自己的演算法而不要懷疑是樣例輸出寫錯了。
評測用例規模與約定
對於30%的評測用例,1 ≤ n ≤ 10,1 ≤ ti ≤ 20;
對於60%的評測用例,1 ≤ n ≤ 100,1 ≤ ti ≤ 100;
對於100%的評測用例,1 ≤ n ≤ 1000,1 ≤ ti ≤ 10000。
解決方法
下面DP演算法複雜為O(n^3)
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
struct node {
int charNum;
int len;
};
#define NMAX 1000
int N[NMAX];
node dp[NMAX][NMAX];
int main()
{
int n;
while (scanf("%d", &n) != EOF) {
memset (dp, 0, sizeof(dp));
int i, j, k;
int minij;
for (i = 0; i < n; ++i)
scanf("%d", N + i);
for (j = 0; j < n; ++j) {
dp[j][j] = { N[j] ,0 };
for (i = j - 1; i >= 0; --i) {
minij = 0x3f3f3f3f;
for (k = i; k < j; ++k)
minij = min(minij, dp[i][k].len + dp[i][k].charNum + dp[k + 1][j].len + dp[k + 1][j].charNum);
dp[i][j] = { dp[i][j - 1].charNum + N[j],minij };
}
}
printf("%d\n", dp[0][n - 1].len);
}
return 0;
}
另附:DP演算法優化版及更為詳細的解答過程,複雜為O(n^2)
[連結](http://blog.csdn.net/qq_35877664/article/details/54708161)