首页 > 代码库 > hdu_5884_Sort(二分+单调队列)

hdu_5884_Sort(二分+单调队列)

题目链接:hdu_5884_Sort

题意:

有n个数,每个数有个值,现在你可以选择每次K个数合并,合并的消耗为这K个数的权值和,问在合并为只有1个数的时候,总消耗不超过T的情况下,最小的K是多少

题解:

首先要选满足条件的最小K,肯定会想到二分。

然后是如何来写这个check函数的问题

我们要贪心做到使消耗最小,首先我们将所有的数排序

然后对于每次的check的mid都取最小的mid个数来合并,然后把新产生的数扔进优先队列,直到最后只剩一个数。

不过这样的做法是n*(logn)2 ,常数写的小,带优化能卡过去,不过我反正卡不过去,然后就需要一个数据结构优化一个log

那就是用单调队列,可以先看看这篇文章  合并果子

然而这里有个小细节,不注意是过不去的:

对于n个数,一共要合并n-1个数,才能最后剩一个数,然后对于每次的mid,每次会合并mid-1个数,如果(n-1)%(mid-1)!=0,那么我们得先取(n-1)%(mid-1)+1个数来合并,这样后面合并的时候才能刚合适,如果不取,读者可以自己模拟一下,会出错。

技术分享
 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 typedef long long ll; 5  6 const int N=1e5+7; 7 int t,n,T,a[N]; 8 ll inf=1e18; 9 queue<ll>Q1,Q2;10 11 bool check(int mid)12 {13     ll ans=0;14     while(!Q1.empty())Q1.pop();15     while(!Q2.empty())Q2.pop();16     F(i,1,n)Q1.push(a[i]);17     int num=(n-1)%(mid-1);18     if(num)19     {20         ll tp=0;21         F(i,1,num+1)tp+=Q1.front(),Q1.pop();22         ans+=tp;23         Q2.push(tp);24     }25     while(1)26     {27         ll tp=0;28         F(i,1,mid)29         {30             ll x=inf,y=inf;31             if(Q1.empty()&&Q2.empty())break;32             if(!Q1.empty())x=Q1.front();33             if(!Q2.empty())y=Q2.front();34             if(x<y)tp+=x,Q1.pop();35             else tp+=y,Q2.pop();36         }37         ans+=tp;38         if(ans>T)return 0;39         if(Q1.empty()&&Q2.empty())break;40         Q2.push(tp);41     }42     return ans<=T;43 }44 45 46 int main(){47     scanf("%d",&t);48     while(t--)49     {50         scanf("%d%d",&n,&T);51         F(i,1,n)scanf("%d",a+i);52         sort(a+1,a+1+n);53         int l=2,r=n,mid,ans;54         while(l<=r)mid=(l+r)>>1,check(mid)?r=mid-1,ans=mid:l=mid+1;55         printf("%d\n",ans);56     }57     return 0;58 }
View Code

 

hdu_5884_Sort(二分+单调队列)