描述
考虑有 n n n个单词,每个单词出现次数为 w i w_i wi。考虑用一个 01 01 01串对其重新编号,使得最终满足 ∀ i , j ( i ≠ j ) \forall i,j(i\neq j) ∀i,j(i=j),满足 s i s_i si不是 s j s_j sj的前缀。每个 01 01 01串的代价为 0 0 0的个数与两倍的 1 1 1的个数之和。
求满足条件的编号的最小代价。
n ≤ 1 0 3 n\leq 10^3 n≤103, w i ≤ 1 0 5 w_i\leq 10^5 wi≤105
分析
构建一棵 T r i e Trie Trie,每个叶子对应一个 01 01 01串,且这些串必定互不为彼此前缀。
令每个节点的左儿子为 0 0 0,边权为 1 1 1;每个右儿子为 1 1 1,边权为 2 2 2。定义一个点的深度为根到这个点路径上权值和。一个 01 01 01串的代价即为代表这个 01 01 01串的叶子的深度。
根据排序不等式可知,将按照深度排序的叶子与按频率排序的单词逆序配对,可使总代价最小。同时为了使答案更优,树上的每个节点要么是叶子,要么有两个儿子。
设 F l , a , b F_{l,a,b} Fl,a,b表示前 i i i层,已经确定不会再拓展的叶子节点有 l l l个, i − 1 i-1 i−1层未确定的叶子结点有 a a a个, i i i层未确定的叶子结点有 b b b个。
分两种情况转移
-
当前位于第 i i i层,将 i − 1 i-1 i−1层中第一个节点变为叶子节点,则
F l + 1 , a − 1 , b ← F l , a , b F_{l+1,a-1,b}\leftarrow F_{l,a,b} Fl+1,a−1,b←Fl,a,b -
当前位于第 i i i层,将 i − 2 i-2 i−2层未确定的节点全拓展出两个儿子,则通过边权为 1 1 1的边,到达第 i − 1 i-1 i−1层的未确定的叶子节点有 a a a个,同理到达第 i i i层的未确定的叶子节点有 a a a个,则
F l , a + b , a ← F l , a , b F_{l,a+b,a}\leftarrow F_{l,a,b} Fl,a+b,a←Fl,a,b
将 w i w_i wi从大到小排序,记 S u m n = ∑ i = 1 n w i \displaystyle Sum_n=\sum_{i=1}^n w_i Sumn=i=1∑nwi。每下降一层,即深度增加,则对于剩余未确定的叶子节点所代表的 01 01 01串来说,代价增加了 1 1 1,乘以这些串的个数,即为深度增加一,代价的增长值。而又因为要与按频率排序的单词逆序配对,故处理一个前缀和,那么增长值即为 S u m l + 1 Sum_{l+1} Suml+1。
发现这个 l l l可以滚动,于是滚动一下就好了。上述的 i i i在转移时无关紧要,故不位于状态中。
时间复杂度 O ( n 3 ) O(n^3) O(n3)
代码
uses math;
var
n,i,j,k,now,nowx:longint;
a:array[0..1001] of longint;
f:array[0..1,-1..1000,0..1000] of longint;
procedure qsort(l,r:longint);
var
i,j,t,mid:longint;
begin
i:=l;
j:=r;
mid:=a[(l+r) shr 1];
while i<j do
begin
while a[i]>mid do inc(i);
while a[j]<mid do dec(j);
if i<=j then
begin
t:=a[i]; a[i]:=a[j]; a[J]:=t;
inc(i); dec(j);
end;
end;
if i<r then qsort(i,r); if l<j then qsort(l,j);
end;
begin
readln(n);
for i:=1 to n do read(a[i]);
qsort(1,n); for i:=n downto 1 do inc(a[i],a[i+1]);
fillchar(f,sizeof(f),120); f[0,1,0]:=0;
for i:=0 to n-1 do
begin
now:=now xor 1; nowx:=now xor 1;
for j:=0 to n-i do
for k:=0 to n-i-j do
if f[nowx,j,k]<>2021161080 then
begin
f[now,j-1,k]:=min(f[now,j-1,k],f[nowx,j,k]);
f[nowx,j+k,j]:=min(f[nowx,j+k,j],f[nowx,j,k]+a[i+1]);
f[now,j,k]:=2021161080;
end;
end;
writeln(f[now,0,0]);
end.