给定一段文字,已知单词 a1,a2,…,ana1,a2,…,an 出现的频率分别 t1,t2,…,tnt1,t2,…,tn。
可以用 0101 串给这些单词编码,即将每个单词与一个 0101 串对应,使得任何一个单词的编码(对应的 0101 串)不是另一个单词编码的前缀,这种编码称为前缀码。
使用前缀码编码一段文字是指将这段文字中的每个单词依次对应到其编码。
一段文字经过前缀编码后的长度为:
L=a1L=a1 的编码长度 ×t1+a2×t1+a2 的编码长度 ×t2+…+an×t2+…+an 的编码长度 ×tn×tn。
定义一个前缀编码为字典序编码,指对于 1≤i<n1≤i<n,aiai 的编码(对应的 0101 串)的字典序在 ai+1ai+1 编码之前,即 a1,a2,…,ana1,a2,…,an 的编码是按字典序升序排列的。
例如,文字 E A E C D E B C C E C B D B E
中, 55 个单词 A、B、C、D、EA、B、C、D、E 出现的频率分别为 1,3,4,2,51,3,4,2,5,则一种可行的编码方案是 A:000, B:001, C:01, D:10, E:11
,对应的编码后的 0101 串为 1100011011011001010111010011000111
,对应的长度 LL 为 3×1+3×3+2×4+2×2+2×5=343×1+3×3+2×4+2×2+2×5=34。
在这个例子中,如果使用哈夫曼(Huffman)编码,对应的编码方案是 A:000, B:01, C:10, D:001, E:11
,虽然最终文字编码后的总长度只有 3333,但是这个编码不满足字典序编码的性质,比如 CC 的编码的字典序不在 DD 的编码之前。
在这个例子中,有些人可能会想的另一个字典序编码是 A:000, B:001, C:010, D:011, E:1
,编码后的文字长度为 3535。
请找出一个字典序编码,使得文字经过编码后的长度 LL 最小。
在输出时,你只需要输出最小的长度 LL,而不需要输出具体的方案。
在上面的例子中,最小的长度 LL 为 3434。
输入格式
输入的第一行包含一个整数 nn,表示单词的数量。
第二行包含 nn 个整数,用空格分隔,分别表示 a1,a2,…,ana1,a2,…,an 出现的频率,即 t1,t2,…,tnt1,t2,…,tn。
请注意 a1,a2,…,ana1,a2,…,an 具体是什么单词并不影响本题的解,所以没有输入 a1,a2,…,ana1,a2,…,an。
输出格式
输出一个整数,表示文字经过编码后的长度 LL 的最小值。
数据范围
对于 30%30% 的评测用例,1≤n≤10,1≤ti≤201≤n≤10,1≤ti≤20;
对于 60%60% 的评测用例,1≤n≤100,1≤ti≤1001≤n≤100,1≤ti≤100;
对于 100%100% 的评测用例,1≤n≤1000,1≤ti≤100001≤n≤1000,1≤ti≤10000。
输入样例:
5
1 3 4 2 5
输出样例:
34
样例解释
这个样例就是问题描述中的例子。如果你得到了 3535,说明你算得有问题,请自行检查自己的算法而不要怀疑是样例输出写错了。
package main
import (
"fmt"
)
func min( a, b int) int {
if a < b {
return a
}
return b
}
func main(){
var n int
fmt.Scanf("%d", &n)
A := make([]int, 1010)
for i := 1 ; i <= n ; i ++ {
fmt.Scanf("%d", &A[i])
A[i] += A[i - 1]
}
f := make([][]int, 1010)
for i := range f {
f[i] = make([]int,1010)
}
for l := 2; l <= n ; l ++ {
for i := 1 ; i + l - 1 <= n; i ++{
j := i + l - 1
f[i][j] = 1000000000
for k := i; k < j ; k ++{
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + A[j] - A[i - 1])
}
}
}
fmt.Println(f[1][n])
}