Alphabet Ak consists of k initial letters of English alphabet. A positive integer called a weight is assigned to each letter of the alphabet. A weight of a word built from the letters of the alphabet Ak is the sum of weights of all letters in this word. A language over an alphabet Ak is any finite set of words built from the letters of this alphabet. A weight of a language is the sum of weights of all its words. We say that the language is prefixless if for each pair of different words w, v from this language w is not a prefix of v.
We want to find out what is the minimal possible weight of an n-element, prefixless language over an alphabet Ak.
Example
Assume that k = 2, the weight of the letter a is W(a) = 2 and the weight of the letter b is W(b) = 5. The weight of the word ab is W(ab) = 2 + 5 = 7. W(aba) = 2 + 5 + 2 = 9. The weight of the language J = {ab, aba, b} is W(J) = 21. The language J is not prefixless, since the word ab is a prefix of aba. The lightest three-element, prefixless language over the alphabet A2 (assuming that weights of the letters are as before) is {b, aa, ab}; its weight is 16.
Task
Write a program that for each test case:
reads two integers n, k and the weights of k letters of an alphabet Ak;
computes the minimal weight of a prefixless, n-element language over the alphabet Ak;
outputs the result.
Input
The number of test cases t is in the first line of input, then t test cases follow separated by an empty line.
In the first line of a test case there are two positive integers n and k separated by a single space, (2 <= n <= 10000, 2 <= k <= 26). These are the number of words in a language and the number of letters in an alphabet respectively. The second line contains k positive integers separated by single spaces. Each of them is not greater than 10000. The i-th number is the weight of the i-th letter.
Output
For each test case you should output one line with the weight of the lightest prefixless n-element language over the alphabet Ak.
Example
Sample input:
1
3 2
2 5
Sample output:
16
提前声明:这个题的代码不是自己写的。
现在来讲一下这道题的思路:
首先我们定义prefixless语言为满足单词互不为前缀的语言,由于给定了值,我们要求权值最小的prefixless语言,就要想办法找出这样的单词,这显然有很多种,所以要用到字典树的一个思想。(这里不进行深入展开)每次可用单词即为字典树叶子节点上的单词。但是我们不需要实际建树,用数组模拟即可。
接下来要去n个单词,使其权值和最小,很自然想到贪心,然后可以用堆来实现。有人可能会误解,认为只要求出n个单词即可停止,因为我们是贪心做的,其实着都是因为样例,我自己写了一组例子,可以让大家看的更清晰。
共三个字母:a=2,b=5,c=100,求4个单词。接下来上一张图(电脑画的,手残勿喷,应该还是可以看懂的)
图中红色的是节点,绿色的是权值,一步一步来:
如果按照最开始所说的找出四个最小的就停止的话,就会出问题,会得到:aa,ab,b,c;权值还是比较大的,继续尝试,发现aaa,aab,ab,b这个解显然要更优秀,再次查找即可发现无法更新值了,所以我们的计算条件即为:拓展一个节点后最优解无法得到更新。
但是事情没有想象的那么简单,由于数据量较大,我们无法存下,会爆空间,所以我们要用到一个最大堆-最小堆优化。这个地方是重点,博主就是这个地方不会写,所以代码是看别人的。因为最多选n个元素嘛,所以堆里当然只要存n个就够了,每次我们选最小的n个,当第n+1个元素出现之后,我们只需要将最大的那个删除即可,所以我们要将元素同时插入到两个堆中,同时插入同时删除。这个优化做完后,这题差不多就可以过了。
代码是看了这样一片博客(貌似也是转的,但是原博客打不开了)http://blog.sina.com.cn/s/blog_89a06c7d0100u2or.html
下面贴上别人的代码:
注意,这个代码我是修改过一部分的,原博客中没有处理多种数据。
#include<cstdio>
#include<cstring>
using namespace std;
const int MaxL=10010;
int N,M;
int a[27];
int Len,ans,sum,Min[MaxL],Max[MaxL],Minid[MaxL],Maxid[MaxL];
inline void swap(int &x,int &y){
int tmp=x;
x=y;
y=tmp;
}
void down_max(int i){
int j=i*2;
while(j<=Len){
if(j<Len&&Max[j+1]>Max[j])
j++;
if(Max[i]<Max[j]){
swap(Max[i],Max[j]);
Minid[Maxid[i]]=j;
Minid[Maxid[j]]=i;
swap(Maxid[i],Maxid[j]);
i=j;
j=i*2;
}
else return;
}
}
void down_min(int i){
int j=i*2;
while(j<=Len){
if(j<Len&&Min[j+1]<Min[j])
j++;
if(Min[i]>Min[j]){
swap(Min[i],Min[j]);
Maxid[Minid[i]]=j;
Maxid[Minid[j]]=i;
swap(Minid[i],Minid[j]);
i=j;
j=i*2;
}
else return;
}
}
void up_max(int j){
int i=j/2;
while(i){
if(Max[i]<Max[j]){
swap(Max[i],Max[j]);
Minid[Maxid[i]]=j;
Minid[Maxid[j]]=i;
swap(Maxid[i],Maxid[j]);
j=i;
i/=2;
}
else return;
}
}
void up_min(int j){
int i=j/2;
while(i){
if(Min[i]>Min[j]){
swap(Min[i],Min[j]);
Maxid[Minid[i]]=j;
Maxid[Minid[j]]=i;
swap(Minid[i],Minid[j]);
j=i;
i/=2;
}
else return;
}
}
void add(int val){
if(Len<N){
Len++;
Min[Len]=val; Max[Len]=val;
Minid[Len]=Len; Maxid[Len]=Len;
up_min(Len); up_max(Len);
sum+=val;
}
else if(Max[1]>val){
sum=sum-Max[1]+val;
int i=Maxid[1];
Max[1]=val;
Min[i]=val;
down_max(1);
down_min(i);
up_min(i);
}
}
void del(){
int i=Minid[1];
sum-=Min[1];
if(1!=Len){
Min[1]=Min[Len];
Minid[1]=Minid[Len];
Maxid[Minid[Len]]=1;
}
if(i!=Len){
Max[i]=Max[Len];
Maxid[i]=Maxid[Len];
Minid[Maxid[Len]]=i;
}
Len--;
down_min(1);
down_max(i);
up_max(i);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
memset(a,0,sizeof(a));
scanf("%d%d",&N,&M);
sum=Len=0;
ans=0x7FFFFFFF;
for(int i=1;i<=M;++i)
scanf("%d",a+i);
for(int i=1;i<M;++i)
for(int j=i+1;j<=M;++j)
if(a[i]>a[j])
swap(a[i],a[j]);
for(int i=1;i<=M;++i)
add(a[i]);
do{
if(Len==N&&ans>sum)
ans=sum;
int t=Min[1];
del();
for(int i=1;i<=M;++i)
add(t+a[i]);
if(Len==N&sum>=ans) break;
}while(1);
printf("%d\n",ans);
}
return 0;
}