P6033 [NOIP2004 提高组] 桶排序 / 基数排序 + 队列

题意

传送门 P6033 [NOIP2004 提高组] 合并果子 加强版

题解

求解体力值对应了一个二叉 H u f f m a n Huffman Huffman 树问题,时间复杂度的瓶颈在于二叉堆每次增添元素的 O ( log ⁡ n ) O(\log n) O(logn) 时间。假设果子重量 a i a_i ai 有序,那么每次合并的果子总重量不小于前一次果子合并的总重量,那么可以使用 2 2 2 个队列分别维护初始有序的果子与合并的果子,每次合并果子插入后者,可以保证 2 2 2 个队列都是有序的。那么每次选取最小重量的果子只用 O ( 1 ) O(1) O(1) 比较 2 2 2 个队列的队首元素。

算法复杂度的瓶颈在于初始对果子重量的排序,需要使用接近线性复杂度的排序算法。

桶排序 + 队列

桶排序的基本思想是在元素值域上按顺序分为一块块区域(桶),将各元素划分到其对应值域范围的区域,然后分别对各个区域的元素进行排序,最后合并各区域元素。设区域个数为 b b b 且元素均匀划分至各区域,排序时间复杂度 O ( b × ( n / b ) × log ⁡ ( n / b ) ) O(b\times (n/b)\times \log (n/b)) O(b×(n/b)×log(n/b)),归并时间复杂度 O ( n ) O(n) O(n)。若元素值域较小,如本题的 [ 1 , 1 0 5 ] [1,10^5] [1,105],可直接划分出与值域规模相当的区域数量,此时排序总时间复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000;
int N, C[maxn + 1];
queue<ll> X, Y;

inline void read(int &x)
{
    x = 0;
    char c = 0;
    for (; c < '0' || c > '9'; c = getchar())
        ;
    for (; '0' <= c && c <= '9'; c = getchar())
        x = (x << 1) + (x << 3) + c - '0';
}

inline ll get()
{
    ll x;
    if (Y.empty() || (X.size() && X.front() < Y.front()))
        x = X.front(), X.pop();
    else
        x = Y.front(), Y.pop();
    return x;
}

int main()
{
    read(N);
    for (int i = 0, a; i < N; ++i)
        read(a), ++C[a];
    for (int i = 1; i <= maxn; ++i)
        while (C[i]--)
            X.push(i);
    ll res = 0;
    for (int i = 1; i < N; ++i)
    {
        ll x = get(), y = get(), s = x + y;
        res += s;
        Y.push(s);
    }
    printf("%lld\n", res);
    return 0;
}
基数排序 + 队列

当元素值域较大时,特别构造的数据可以使桶排序时间复杂度退化为 O ( n log ⁡ n ) O(n\log n) O(nlogn),此时使用基数排序是一种可能的更优方案。

基数排序的基本思想是将元素看做 b b b 进制数字,由于高位数字权重更大,从低位向高位处理,每次对当前这一位进行排序,由于 b b b 相对于元素值域较小,采用桶排序 O ( b ) O(b) O(b) 处理。总时间复杂度 O ( ( n + b ) log ⁡ b max ⁡ i = 0 n − 1 { a i } ) O((n+b)\log_{b}^{\max_{i=0}^{n-1}\{a_i\}}) O((n+b)logbmaxi=0n1{ai})。一般取 b = 2 k b=2^k b=2k,使用位运算代替取模运算高效地求出元素 b b b 进制下某一位的值;合适地选取 b b b,可以使复杂度接近线性复杂度。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10000005;
int N, A[maxn], B[maxn];
queue<ll> X, Y;

inline void read(int &x)
{
    x = 0;
    char c = 0;
    for (; c < '0' || c > '9'; c = getchar())
        ;
    for (; '0' <= c && c <= '9'; c = getchar())
        x = (x << 1) + (x << 3) + c - '0';
}

void radix_sort(int *a, int n)
{
    int cnt[256];
    for (int i = 0; i < 17; i += 8)
    {
        memset(cnt, 0, sizeof(cnt));
        for (int j = 0; j < n; ++j)
            ++cnt[a[j] >> i & 255];
        for (int j = 1; j < 256; ++j)
            cnt[j] += cnt[j - 1];
        for (int j = n - 1; j >= 0; --j)
            B[--cnt[a[j] >> i & 255]] = a[j];
        memcpy(a, B, sizeof(int) * n);
    }
}

inline ll get()
{
    ll x;
    if (Y.empty() || (X.size() && X.front() < Y.front()))
        x = X.front(), X.pop();
    else
        x = Y.front(), Y.pop();
    return x;
}

int main()
{
    read(N);
    for (int i = 0; i < N; ++i)
        read(A[i]);
    radix_sort(A, N);
    for (int i = 0; i < N; ++i)
        X.push(A[i]);
    ll res = 0;
    for (int i = 1; i < N; ++i)
    {
        ll x = get(), y = get(), s = x + y;
        res += s;
        Y.push(s);
    }
    printf("%lld\n", res);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值