1. 程式人生 > >hdu 5884 二分+佇列

hdu 5884 二分+佇列

連結:戳這裡

Sort Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Problem Description
Recently, Bob has just learnt a naive sorting algorithm: merge sort. Now, Bob receives a task from Alice.
Alice will give Bob N sorted sequences, and the i-th sequence includes ai elements. Bob need to merge all of these sequences. He can write a program, which can merge no more than k sequences in one time. The cost of a merging operation is the sum of the length of these sequences. Unfortunately, Alice allows this program to use no more than T cost. So Bob wants to know the smallest k to make the program complete in time.
 
Input
The first line of input contains an integer t0, the number of test cases. t0 test cases follow.
For each test case, the first line consists two integers N (2≤N≤100000) and T (∑Ni=1ai<T<231).
In the next line there are N integers a1,a2,a3,...,aN(∀i,0≤ai≤1000).
 
Output
For each test cases, output the smallest k.
 
Sample Input
1
5 25
1 2 3 4 5
 
Sample Output
3
 

題意:


n
n個有序序列的歸併排序。每次可以選擇不超過kk個序列進行合併,合併代價為這些序列的長度和.總的合併代價不能超過TT, 問kk最小是多少。

思路:

1:首先想到的是二分這個答案k,然後check每個k是否可行

2:對於每個k,總共需要歸併n-1個數,每次歸併k-1個數

所以當(n-1)%(k-1)!=0的時候,會出現歸併不能最大化個數的情況,這樣會影響二分的單調性

我們先取(n-1)%(k-1)個小的數湊成一次k

      接下來的數我們已經知道了肯定是(n-1)/(k-1)塊了,所以可以直接想到放進優先佇列,然後每次都拿前k個數,新的數繼續丟進去,但是這樣會T

      那麼?我們這樣處理,將其餘的數丟進佇列qu1中,再開一個佇列qu2,每次新的數丟進qu2中。每次再qu1和qu2中一共拿k個小的數來組成一個新的數,新的數丟到qu2隊尾去。接下來證明為什麼不丟進優先佇列,反而佇列就可以了(也就是為什麼qu2都是排好序的?),每次新丟進來數都是由前k個小的數取和得來的,所以每次丟進隊尾的數都比前面丟進去的數要大,隨手畫一下就可以了,然後拿完qu1再去拿qu2。

程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<iomanip>
#include<cmath>
#include<bitset>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef long double ld;
#define INF (1ll<<60)-1
#define Max 1e9
using namespace std;
ll a[200100],T;
int n;
queue<int> qu1,qu2;
bool pd(int k){
    while(!qu1.empty()) qu1.pop();
    while(!qu2.empty()) qu2.pop();
    for(int i=1;i<=n;i++) qu1.push(a[i]);
    int num=0;
    ll sum=0,ans=0;
    if((n-1)%(k-1)!=0){
        num=(n-1)%(k-1)+1;
        for(int i=1;i<=num;i++) {
            sum+=qu1.front();
            qu1.pop();
        }
        qu2.push(sum);
        ans+=sum;
    }
    while(!qu1.empty()){
        sum=0;
        for(int i=1;i<=k;i++){
            if(!qu1.empty() && !qu2.empty() ){
                if(qu1.front()<=qu2.front()){
                    sum+=qu1.front();
                    qu1.pop();
                } else {
                    sum+=qu2.front();
                    qu2.pop();
                }
            } else if(qu1.empty()){
                sum+=qu2.front();
                qu2.pop();
            } else if(qu2.empty()){
                sum+=qu1.front();
                qu1.pop();
            }
        }
        ans+=sum;
        qu2.push(sum);
    }
    if(ans>T) return false;
    sum=0;num=0;
    while(!qu2.empty()){
        sum+=qu2.front();
        qu2.pop();
        num++;
        if(num==k){
            qu2.push(sum);
            ans+=sum;
            sum=0;
            num=0;
            if(qu2.size()==1) break;
        }
    }
    if(ans>T) return false;
    return true;
}
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%I64d",&n,&T);
        for(int i=1;i<=n;i++) scanf("%I64d",&a[i]);
        sort(a+1,a+n+1);
        int l=2,r=n,mid,ans=1;
        while(l<=r){
            mid=(l+r)/2;
            if(pd(mid)) {
                r=mid-1;
                ans=mid;
            } else l=mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}