天天看點

HDU 5884 Sort 二分+優先隊列/單調隊列Sort

Sort

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 4449    Accepted Submission(s): 1098

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

Source

2016 ACM/ICPC Asia Regional Qingdao Online

題意

給出一個n個數的序列,現在要求最小的k滿足 每次最多取出k個數進行合并且合并的總代價小于等于t

合并的代價被認為是合并序列的和。

題解

我們考慮二分來解決這個問題

二分一個k

如果我們使用優先隊列每次選出k個數進行合并,這是最優的合并方式。

當然這必須滿足每次取數恰好為k個。否則需要把多出來的數提前合并了。

通過(n-1) % (k-1) 進行判斷

使用優先隊列的check需要O(nlogn)時間複雜度。

總共需要O(nlognlogn)的時間複雜度,對于題給資料優化下輸入勉強能卡過去。

代碼

/**
     * 二分 mid O(logn) check O(nlogn) 時間複雜度 O(nlognlogn) 
     * 100000 * log(100000) * log(100000) = 2e7 
     * 1s TLE
     * 
     * 同時需要注意的是貪心的時候,對于不能每次都保證取k次能取光的時候,我們需要将多餘的先優先合并了。
     * 如 (n-1) % (k-1) == 0 表示每次都能保證隻取k個。
     * 否則需要先将 (n-1) % (k-1) + 1 個先合并了。
     * 為什麼是這樣的呢,我們這麼來考慮這個問題,每次取k個合并完後又要放進一個,相當于每次隻取走了k-1個。
     * 同時合并隻剩下1個的時候停止,于是是(n-1)
     * 綜上 考慮 (n-1) % (k-1) 即可 
     * 
     * 考慮單調隊列優化這個問題
     *
     */
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    priority_queue<ll,vector<ll>,greater<ll> > qu;
    const int maxn = 1e5+10;
    ll arr[maxn],t;
    int n;
    namespace IO
    {
        template <typename T>
        inline bool scan_d (T &ret)
        {
            char c;
            int sgn;
            if (c = getchar(), c == EOF)return false; //EOF
            while (c != '-' && (c < '0' || c > '9') )
                if((c = getchar()) == EOF) return false;
            sgn = (c == '-') ? -1 : 1;
            ret = (c == '-') ? 0 : (c - '0');
            while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
            ret *= sgn;
            return true;
        }

        template<typename T>
        void print(T x)
        {
            static char s[33], *s1;
            s1 = s;
            if (!x) *s1++ = '0';
            if (x < 0) putchar('-'), x = -x;
            while(x) *s1++ = (x % 10 + '0'), x /= 10;
            while(s1-- != s) putchar(*s1);
        }

        template<typename T>
        void println(T x)
        {
            print(x);
            putchar('\n');
        }
    };
    bool check(int k) 
    {
        ll ans = 0;
        while(!qu.empty()) qu.pop();
        for(int i=1;i<=n;i++) qu.push(arr[i]);
        int ma = (n-1) % (k-1);
        if(ma) {
            ll temp = 0;
            for(int i=1;i<=ma+1 && !qu.empty();i++) temp += qu.top(),qu.pop();
            ans += temp;
            qu.push(temp);
        }
        while(!qu.empty()) {
            ll temp = 0;
            for(int i=1;i<=k && !qu.empty();i++) temp += qu.top(),qu.pop();
            ans += temp;
            if(!qu.empty()) qu.push(temp);
            //if(ans > t) return false;
        }
        return ans <= t;
    }
    int main()
    {
        int caset;IO::scan_d(caset);
        while(caset--) {
            IO::scan_d(n);IO::scan_d(t);
            for(int i=1;i<=n;i++) IO::scan_d(arr[i]);
            std::sort(arr+1,arr+1+n); //先排序,減少建堆時間
            int l = 2,r = n;
            while(l <= r) {
                int mid = (l+r)>>1;
                if(check(mid)) r = mid - 1;
                else l = mid + 1;
            }
            IO::println(l);
        }
        return 0;
    }
           

HDU 5884 Sort 二分+優先隊列/單調隊列Sort

題解

由于合并後的生成的數具有單調性,那麼我們采用單調隊列的思路,我們用兩個隊列,一個是原數列,另一個存放合并和生成的數。

每次從兩個隊列取小合并。check()時間複雜度O(n) 總時間複雜度O(nlogn)

代碼

/**
 * 前面說到優先隊列的做法是 O(nlognlogn)的顯然這裡明顯是會TLE的
 * 而單調隊列在這裡做的事情就是優化掉一個log
 * 使其變成O(nlogn)即在O(n)的時間進行check
 * 
 * 即設立兩個隊列 一個是排好序的原數列,另一個是合并後的數的序列.(因為合并生成的數滿足單調遞增性)
 * 那麼我們隻需要取數時,從兩個序列取小取出即可.
 */
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
// struct Queue
// {
//     int a[maxn],head,tail;
//     Queue() {
//         head = tail = 0;
//     }
//     void init() {
//         head = tail = 0;
//     }
//     bool empty() {
//         return head == tail;
//     }
//     int front() {
//         return a[tail-1];
//     }
//     void pop() {
//         head++;
//     }
//     void push(int x) {
//         a[tail++] = x;
//     }
// }qu[2];
ll arr[maxn];
int n;ll t;
bool check(int mid) {
    ll ans = 0;
    // qu[0].init();qu[1].init();
    queue<int> qu[2];
    for(int i=1;i<=n;i++) qu[0].push(arr[i]);
    int ma = (n-1) % (mid-1);
    if(ma) {
        ll temp = 0;
        for(int i=1;i<=ma+1 && !qu[0].empty();i++) temp += qu[0].front(),qu[0].pop();
        ans += temp;
        qu[1].push(temp); 
    }
    while(1) {
        if(qu[0].empty() && qu[1].empty()) break;
        ll temp = 0;
        for(int i=1;i<=mid;i++) {
            if(!qu[0].empty()) {
                if(!qu[1].empty()) {
                    if(qu[0].front() < qu[1].front()) {
                        temp += qu[0].front();qu[0].pop();
                    }
                    else {
                        temp += qu[1].front();qu[1].pop();
                    }
                }
                else {
                    temp += qu[0].front();qu[0].pop();
                }
            }
            else {
                temp += qu[1].front();qu[1].pop();
            }
        }
        ans += temp;
        if(qu[0].empty() && qu[1].empty()) break;
        qu[1].push(temp);
        if(ans > t) return false;
    }
    return ans <= t;
}
int main()
{
    int caset;scanf("%d",&caset);
    while(caset--)
    {
        scanf("%d%lld",&n,&t);
        for(int i=1;i<=n;i++) scanf("%lld",&arr[i]);
        std::sort(arr+1,arr+n+1);
        int l=2,r=n;
        while(l <= r) 
        {
            int mid = (l+r)>>1;
            if(check(mid)) r = mid - 1;
            else l = mid + 1;    
        }
        printf("%d\n",l);
    }
    return 0;
}
           
HDU 5884 Sort 二分+優先隊列/單調隊列Sort

明顯時間優化了不少。