• [dp] Jzoj P6012 荷马史诗


    Description

           追逐影子的人,自己就是shadow。 ——荷马
           Shadow 最近迷上了文学。她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的《荷马史诗》。但是由《奥德赛》和《伊利亚特》组成的鸿篇巨制《荷马史诗》实在是太长了,Shadow 想通过一种编码方式使得它变得短一些。
           一部《荷马史诗》中有 n 种不同的单词,从 1 到 n 进行编号。其中第 i 种单词出现的总次数为 wi。Shadow 想要用 2 进制串 si 来替换第 i 种单词,使得其满足如下要求:
           对于任意的 1 ≤ i, j ≤ n,i 6= j,都有:si 不是 sj 的前缀。
           现在 Shadow 想要知道,如何选择 sj,才能使替换以后得到的新的《荷马史诗》代价最小。一本书的代价为cnt(0) + 2 ∗ cnt(1),其中cnt(x)串中x字符的出现次数。
           一个字符串被称为二进制字符串,当且仅当它的每个字符是 0 或 1。
           字符串 Str1 被称为字符串 Str2 的前缀,当且仅当:存在 1 ≤ t ≤ m,使得Str1 = Str2[1..t] 。其中,m 是字符串 Str2 的长度,Str2[1..t] 表示 Str2 的前 t个字符组成的字符串。
     

    Input

    从文件b.in中读入数据.
    输入文件的第 1行包含 1 个正整数 n,表示共有 n 种单词。
    接下来 1 行,包含 n 个非负整数,第i个整数代表 wi ,表示第 i 种单词的出现次数。

    Output

    输出到文件b.out中.
    输出 1 个整数,为《荷马史诗》经过重新编码以后的最短长度。
     

    Sample Input

    Sample Input1
    4
    1 1 2 2
    
    Sample Input2
    6
    1 1 3 3 9 9

    Sample Output

    Sample Output1
    17
    
    Sample Output2
    81
     
     

    Data Constraint

    对于所有数据,保证2 ≤ n ≤ 1000, 1 ≤ wi ≤ 10^5
    • 对于前15%的数据,n ≤ 15
    • 对于另10%的数据,∀wi 相等
    • 对于另25%的数据,n ≤ 100
    • 对于另20%的数据,n ≤ 400
    • 对于另10%的数据,n ≤ 750
    • 对于最后20%的数据,没有特殊的约定.

    题解

    • 假设,我们把每个s打进一棵Trie里,把0当成边权为1,把1当成边权为2,那么答案其实就是要使∑w[i]*dep[i]最小
    • 那么要使答案越小,那么显然就是把出现次数多的,也就是w[i]大的先打进Trie里,那么就先将w从大到小排序
    • 对于Trie树上的点,要不就是叶子节点,要不就有两个儿子,一个延申一层,一个延伸两层
    • 那么这样的话,我们在DP里只用知道当前层的状态和上一层的状态就行了
    • 设f[i][j][k]为当前在第i层,上一层有j个空叶子,当前层有k个空叶子的最小答案
    • 有两种转移:①把当前层填入上一层的某个叶子f[i+1][j-1][k]=min(f[i-1][j][k]) ②把上一层的j个叶子扩展,左右都拓展j个,左拓展深度1,右拓展深度2,那么上一层就有j+k个叶子,当前层就有j个叶子 f[i][j+k][k]=min(f[i][j][k]+w[n-i])
    • 然后我们可以发现一个问题,如果这样存的话f数组的大小就到了N^3,这样显然会炸的飞起
    • 再返回去看DP,f[i][j][k]显然只会转移到下一层,简单点说也就是i只会转移到i+1,这样的话就可以用滚动数组来做
    • 时间复杂度的话,O(n^3)???(大佬貌似说跑不满)

    代码

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstring>
     4 #include <algorithm>
     5 #define ll long long
     6 using namespace std;
     7 const ll N=1e3+10,inf=0x7fffffff;
     8 ll f[3][N][N],a[N],n,l,r;
     9 int main()
    10 {
    11     freopen("b.in","r",stdin),freopen("b.out","w",stdout),scanf("%lld",&n);
    12     for (ll i=1;i<=n;i++) scanf("%lld",&a[i]);
    13     for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) f[0][i][j]=inf;
    14     sort(a+1,a+n+1),f[0][1][0]=0;
    15     for (ll i=2;i<=n;i++) a[i]+=a[i-1];
    16     for (ll i=0;i<n;i++)
    17     {
    18         l=i&1,r=l^1;
    19         for (ll j=0;j<=n-i;j++) for (ll k=0;k<=n-i-j;k++) f[r][j][k]=inf;
    20         for (ll j=0;j<=n-i;j++)
    21             for (ll k=0;k<=n-i-j;k++)
    22                 if (f[l][j][k]!=inf)
    23                 {
    24                     if (j) f[r][j-1][k]=min(f[r][j-1][k],f[l][j][k]);
    25                     if (j+k) f[l][j+k][j]=min(f[l][j+k][j],f[l][j][k]+a[n-i]);
    26                 }
    27     }
    28     printf("%lld",f[r][0][0]);
    29 }
  • 相关阅读:
    正向代理和反向代理
    Unicode
    utf-8
    ISO 8895-1
    ProtocalBuffers学习记录
    C#基础知识
    MSBuild学习记录
    Linux学习笔记
    Jenkins学习记录
    CruiseControl.Net学习记录
  • 原文地址:https://www.cnblogs.com/Comfortable/p/10321783.html
Copyright © 2020-2023  润新知