ccf 201612-4 压缩编码
问题分析:
解决本问题,首先需要知道哈夫曼编码。参见:哈夫曼编码_百度百科。
这是一个编码问题,似乎可以用哈夫曼编码来解决,但是略有不同的地方在于“每个字符的编码按照字典序排列后的顺序与原先顺序一样”。
所以无法每次取出权值最小的两个节点,而只能选择相邻的节点,到底选择哪两个相邻节点,这便是石子问题
设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)。
1 #include
2 #include
3 using namespacestd;4 const int maxn = 1000+5;5 const int INF = 0x7f7f7f7f;6 intdp[maxn][maxn];7 int sum[maxn],num[maxn];//sum为1~i的总权重
8 intn;9
10 intmain()11 {12 cin>>n;13 for(int i=1;i<=n;i++)14 {15 cin>>num[i];16 }17 sum[0] = 0;18 memset(dp,INF,sizeof(dp));19 for(int i=1;i<=n;i++)20 {21 sum[i] = sum[i-1] +num[i];22 dp[i][i] = 0;23 }24 for(int i=2;i<=n;i++)25 {26 for(int j=1;j<=n-i+1;j++)27 {///填写dp[j][m]
28 int m = j+i-1;//纵坐标29 //k为划分点
30 for(int k=j;k