码迷,mamicode.com
首页 > 其他好文 > 详细

[dp] Jzoj P6012 荷马史诗

时间:2019-01-25 22:43:32      阅读:170      评论:0      收藏:0      [点我收藏+]

标签:strong   col   std   延伸   转移   min   pre   can   不同   

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 }

 

[dp] Jzoj P6012 荷马史诗

标签:strong   col   std   延伸   转移   min   pre   can   不同   

原文地址:https://www.cnblogs.com/Comfortable/p/10321783.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!