2016 ACM-ICPC 青岛站网络赛G题 题解
【参考博客】【https://blog.csdn.net/Tawn0000/article/details/82255682】
题意:
将n个数按照每k个一组来合并,合并需要花费的cost是两个数的长度和,问:在T的消费内将所有的数合并所需的最小的k。
分析:
合并之前要处理一下零头,因为每次取k个一直到最后一步剩下的数的个数可能会少于k个,这样的结果就是合并的cost更大了,举个例子:1 2 3 4 5 6,k=4时,先选1 2 3 4然后再5 6 10 是31,但是因为零头有3,先处理3个零头,即:先取1 2 3然后是4 5 6 6 ,是27。显然27<31。
和被参考的那边博主一样,我也是一开始按照二分+优先队列的思路来进行考虑的,最后在O(n logn logn)的复杂度下TLE了。
如果采用双队列(一个队列存原来的数(从小到大排序后再入队),记为队列p1,另一个队列存合并后的数,记为队列p2),时间复杂度是O(n logn)。
双队列具体操作是取p1中k个原来的数相加,加完之后的值和p2队列取出的值数比较,取小的数加入p2(记得pop掉被取队列中的数),因为原队列的数按照从小到大的顺序排列的,所以一定会满足先入队的数比后入队的数小!。这样一来就会使得参与合并的数总是从小的开始。
AC code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 queue<int> q1,q2; 5 int a[100010]; 6 int n,T; 7 bool P(int mid) 8 { 9 while(q1.size()) q1.pop(); 10 while(q2.size()) q2.pop(); 11 for(int i=0;i<n;i++) q1.push(a[i]); 12 ll res=0; 13 int t=(n-1)%(mid-1); 14 if(t) 15 { 16 int p=0; 17 for(int i=0;i<t+1&&!q1.empty();i++) 18 { 19 p+=q1.front(); 20 q1.pop(); 21 } 22 res+=p; 23 q2.push(p); 24 } 25 while(1) 26 { 27 int p=0; 28 for(int i=0;i<mid;i++) 29 { 30 int x=99999999,y=99999999; 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) 35 { 36 p+=x; 37 q1.pop(); 38 } 39 else 40 { 41 p+=y; 42 q2.pop(); 43 } 44 } 45 res+=p; 46 if(res>T) return true; 47 if(q1.empty()&&q2.empty()) break; 48 q2.push(p); 49 } 50 if(res>T) return true; 51 else return false; 52 } 53 int main() 54 { 55 //freopen("input.txt","r",stdin); 56 int t; 57 scanf("%d",&t); 58 while(t--) 59 { 60 scanf("%d%d",&n,&T); 61 for(int i=0;i<n;i++) 62 { 63 scanf("%d",&a[i]); 64 } 65 sort(a,a+n); 66 int sd=1,ed=n; 67 while(ed - sd > 1) 68 { 69 int mid=sd+(ed-sd)/2; 70 if(P(mid)) sd=mid; 71 else ed=mid; 72 } 73 printf("%d\n",ed); 74 } 75 }