CCF CSP 2016年12月第4题 压缩编码(区间DP)

来源:互联网 发布:机器人离线编程软件 编辑:程序博客网 时间:2024/04/28 10:54

问题描述
试题编号:201612-4试题名称:压缩编码时间限制:3.0s内存限制:256.0MB问题描述:
问题描述
  给定一段文字,已知单词a1a2, …, an出现的频率分别t1t2, …, tn。可以用01串给这些单词编码,即将每个单词与一个01串对应,使得任何一个单词的编码(对应的01串)不是另一个单词编码的前缀,这种编码称为前缀码。
  使用前缀码编码一段文字是指将这段文字中的每个单词依次对应到其编码。一段文字经过前缀编码后的长度为:
  L=a1的编码长度×t1+a2的编码长度×t2+…+ an的编码长度×tn
  定义一个前缀编码为字典序编码,指对于1 ≤ nai的编码(对应的01串)的字典序在ai+1编码之前,即a1a2, …, 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个整数,用空格分隔,分别表示a1a2, …, an出现的频率,即t1t2, …, tn。请注意a1a2, …, an具体是什么单词并不影响本题的解,所以没有输入a1a2, …, 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。

解题思路:第一次看到这道题的时候,我还是有点懵逼的。本来想着先用Huffman来做,然后再排个序,后来发现事情并没有这么简单。因为要求编码长度最小并且是字典序,而Huffman压缩是每次都合并当前最小的两个数,这就导致了最终编码的无序性,所以只有每次合并相邻的两个数才能保证字典序,每一次合并的代价是相邻两个数之和。诶,这不就是一个石子归并的问题吗,令dp[i, j]表示合并区间[i, j]里的所有数字的最小代价,很显然,dp[i, i] = 0;而dp[i, j] = min(dp[i, j],dp[i, k] + dp[k+1, j] + sum[i, j])。有了状态转移方程,代码就很好写了。

代码如下:

#include <cstdio>#include <climits>#include <cstring>#include <algorithm>#define MAXN 1005using namespace std;int dp[MAXN][MAXN];int sum[MAXN],arr[MAXN];int main(void) {    int n;    while(scanf("%d",&n) != EOF) {        memset(sum,0,sizeof(sum));        for(int i = 1; i <= n; i++) {            scanf("%d",&arr[i]);            sum[i] = sum[i - 1] + arr[i];        }        memset(dp,0,sizeof(dp));        for(int t = 1; t < n; t++) {            for(int i = 1; i <= n - t; i++) {                int j = i + t;                dp[i][j] = INT_MAX;                for(int k = i; k < j; k++) {                    dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]);                }            }        }        printf("%d\n",dp[1][n]);    }    return 0;}


阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 想妈妈想哭了怎么办啊 怀孕初期hcg翻倍不好怎么办 买了c类的衣服怎么办 主页被360串改怎么办? 入职体检查乙肝怎么办 入职体检两对半怎么办? 入职体检肝功能异常怎么办 有乙肝怀孕建卡怎么办 重修的课有冲突怎么办 苹果7硬件坏了怎么办 发票记账联丢了怎么办 作废的发票没了怎么办 键盘esc键坏了怎么办 cad中esc键失灵怎么办 出家人老了病了怎么办 宜家吸盘吸不住怎么办 车内吸盘吸不住怎么办 水里吸盘吸不住怎么办 摩托车脱审4年怎么办 摩托车8年没年检怎么办 摩托车4年没年检怎么办 38度5算高烧吗怎么办 刚发现怀孕不想要怎么办 被蜥蜴咬出血了怎么办 去韩国打工签证怎么办多么钱 厕所堵了怎么办有妙招 蹲式厕所老是堵怎么办 下蹲式厕所堵了怎么办 蹲的厕所堵住了怎么办 厕所通了又堵怎么办 蹲式厕所经常堵怎么办 厕所下水管堵了怎么办 厕所堵了水满了怎么办 拉屎把厕所堵了怎么办 厕所堵了不下水怎么办 坐厕所堵了怎么办妙招 火车上丢了东西怎么办 网购的东西丢了怎么办 在酒店丢了东西怎么办 我好懒不想工作怎么办 被宠物刺猬咬了怎么办