Gym100212C Order-Preserving Codes

Gym100212C Order-Preserving Codes

\(n\) 种元素,给出每个元素的出现次数 \(a_i\) ,求一种 \(01\) 编码方案使得满足以下条件:

  1. 没有一个编码是另一个的前缀
  2. 最小化编码后文本的总长
  3. 编码后按字典序保持原来的顺序

输出编码方案

\(n\leq2000\)

dp,四边形不等式


可以发现若只有前两个限制就是求哈夫曼编码(但这和本题并没有什么关系

\(s_i=\displaystyle\sum_{k=1}^ia_i\)\(f_{i,\ j}\) 表示将区间 \([l,\ r]\) 的元素的编码建树后的最小权重和

\(f_{i,\ j}=\displaystyle\min_{i\leq k<j}\{f_{i,\ k}+f_{k+1,\ j}+s_j-s_{i-1}\}\) ,即将 \([i,\ k]\) 补上 \(0\) ,将 \([k+1,\ j]\) 补上 \(1\) ,这满足字典序递增

可以发现这满足四边形不等式,于是可以优化到 \(O(n^2)\)

时间复杂度 \(O(n^2)\)

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 2010;
string s;
int n, pos[maxn][maxn];
ll sum[maxn], f[maxn][maxn];

void print(int l, int r) {
  if (l == r) {
    cout << s << endl; return;
  }
  s += '0', print(l, pos[l][r]), s.pop_back();
  s += '1', print(pos[l][r] + 1, r), s.pop_back();
}

int main() {
  freopen("codes.in", "r", stdin);
  freopen("codes.out", "w", stdout);
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%I64d", sum + i);
    pos[i][i] = i, sum[i] += sum[i - 1];
  }
  for (int i = 2; i <= n; i++) {
    for (int j = 1; i + j - 1 <= n; j++) {
      int l = j, r = i + j - 1;
      for (int k = pos[l][r - 1]; k <= pos[l + 1][r]; k++) {
        if (!pos[l][r] || f[l][k] + f[k + 1][r] < f[l][r]) {
          f[l][r] = f[l][k] + f[k + 1][r], pos[l][r] = k;
        }
      }
      f[l][r] += sum[r] - sum[l - 1];
    }
  }
  print(1, n);
  fclose(stdin), fclose(stdout);
  return 0;
}

转载于:https://www.cnblogs.com/Juanzhang/p/11275034.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值