CCF 压缩编码

题目大意:给一个整数n,接下来n个整数,分别表示每个字符的出现频率(按顺序给出),要求你给出一种编码方式,使得每个字符的编码按照字典序排列后的顺序与原先顺序一样,在此前提下希望整体编码长度最小。输出这个最小长度。

题解:看到编码长度最小,一般就会想到哈弗曼编码,何况题目也提了一下哈弗曼编码,但实际上这是个坑。问题就出在这个前提上:每个字符的编码按照字典序排列后的顺序与原先顺序一样。所以我们无法每次取出权值最小的两个节点,而只能选择相邻的节点,至于到底选择哪两个相邻节点,这便是石子问题。

设dp[i][j]表示第i到第j堆石子合并的最优值,sum[i][j]表示第i到第j堆石子的总数量。那么就有状态转移公式:

1、dp[i][j]=0 (i==j)

2、dp[i][j]=min(dp[i][k]+dp[k][j])+sum[i][j] (i!=j)

此时算法复杂为O(n^3),这里可以利用平行四边形优化降为O(n^2):

由上面的方程式可知我们每次求dp[i][j]的关键是找到合适的k值,设p[i][j]为dp[i][j]的这个合适的k值,根据平行四边形规则有以下不等式:p[i][j-1]<=p[i][j]<=p[i+1][j]。

那么求解dp[i][i+L](L为长度)的复杂度就为:

(p[2,L+1]-p[1,L])+(p[3,L+2]-p[2,L+1])…+(p[n-L+1,n]-p[n-L,n-1])=p[n-L+1,n]-p[1,L]≤n。

复杂度为O(n)。然后L从1循环至n,总复杂度就为O(n^2)。


代码如下:

#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
const int inf=0x08080808;
int mmin(int a,int b){
    return aval) {
                dp[i][j]=val;
                p[i][j]=k;
            }
        }
        //cout<


你可能感兴趣的:(CCF)