题目链接: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 }
时间: 2024-08-01 10:44:35