在比赛的时候做这道题目想到了二分也考虑到了K叉Huffman,但是没有想到双队列实现,用的是priority_queue。。结果TLE,很可惜呀。比赛完之后看题解,据说开了加速输入可以勉强AC,可是后来我开了加速输入,一样TLE。。可能是代码写的差的缘故。。。不过学会了双队列实现也还是很不错的。
二分的话,我觉得只要做过一些有关于二分的题目的话,应该是很好想到二分的,就是要求在一段区间里面找到某一个值嘛,我原来写过一篇博客,博客里面也有一个二分的题目,对于二分还不是很熟悉的同学可以去看一看 链接:http://blog.csdn.net/good_night_sion_/article/details/52012208
接下来解决双队列实现的问题,双队列实现的话,我们可以先看这样一个例子,我觉得在当初讲优先队列的时候应该都会讲过的一道合并果子的问题
(链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1588)双队列的做法是这样子的:
先给果子排个序,然后把这些果子统统+到一个队列q里面去,此时在队列里面果子的数量是递增的,此外还有一个叫做sum的队列用于存放合并的果子,每一次取两个果子,取第一个果子的时候选择q和sum前面小的那一个,取第二个的时候也是这样,更新答案,然后把合并后的果子加到sum里面去。因为最开始果子是按照递增序的,我们每一次都是取最小的两个合并后加到sum里面去,因此无论是在sum还是q里面,果子的数量都是递增的,下面是代码:
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
queue<int> q,sum;
int arr[1010],n,ans,te;
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&arr[i]);
sort(arr+1,arr+n+1);
for(int i=1;i<=n;++i)
q.push(arr[i]);
while(q.size()+sum.size()!=1){
for(int i=1;i<=2;++i){
if(q.empty())
te+=sum.front(),sum.pop();
else if(sum.empty())
te+=q.front(),q.pop();
else if(q.front()>sum.front())
te+=sum.front(),sum.pop();
else
te+=q.front(),q.pop();
}
ans+=te;
sum.push(te);
//printf("%d aa\n",te);
te=0;
}
printf("%d\n",ans);
ans=0;
while(!sum.empty())sum.pop();
}
return 0;
}
那么再回到我们这个问题上面来,我们这儿合并的问题是类似的,不过不同的就是我们一次合并mid个(二分时候的mid),而不是2个,虽然说方法差不多,但是还有一个问题需要注意,那就是最优解。我们每一次拿出k个,然后放进去1个,相当于原来的果子减少了k-1个,万一总的果子数不能被表示成为1+n*(k-1)的时候呢?这时候产生了余数,我们需要把这个余数堆以及那个额外的一堆合并成为新的一堆,来转化到我们这个情况。为什么这样子比直接合并k堆,最后再来处理余数堆要优呢?稍微想一想就可以知道,直接合并k堆的情况是有一些堆是多合并了的,多合并了(k-(余数+1))堆,可以想一想。以下是代码:
PS:在赛后写着到题目的时候依然RE了好久。。。。原来是一个变量重复使用所致,还是要细心一些呀
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
queue<int> q1,sum;
int times,t,n,arr[100010],le,ri,mid,q;
bool judge(int num);
int main(){
scanf("%d",×);
while(times--){
scanf("%d%d",&n,&t);
for(int i=1;i<=n;++i)
scanf("%d",&arr[i]);
sort(arr+1,arr+n+1);
le=2,ri=n,mid=(le+ri)>>1;
while(ri-le>1){
if(judge(mid))
ri=mid;
else
le=mid;
mid=(le+ri)>>1;
}
printf("%d\n",(le==ri||judge(le))?le:ri);
}
return 0;
}
bool judge(int num){
while(!q1.empty())
q1.pop();
while(!sum.empty())
sum.pop();
int te=(n-1)%(num-1),ans=0;
if(te>0){
for(int i=1;i<=te+1;++i)
ans+=arr[i];
sum.push(ans);
}
for(int i=te+2;i<=n;++i)
q1.push(arr[i]);
te=0;
while(q1.size()+sum.size()>=num){
for(int i=1;i<=num;++i){
if(q1.empty())
te+=sum.front(),sum.pop();
else if(sum.empty())
te+=q1.front(),q1.pop();
else if(q1.front()>sum.front())
te+=sum.front(),sum.pop();
else
te+=q1.front(),q1.pop();
}
sum.push(te),ans+=te,te=0;
}
return ans<=t;
}