Hdu 5884

hdu 5884 Sort

题意:

n个有序序列的归并排序。每次可以选择不超过k个序列进行合并,合并代价为这些序列的长度和,总的合并代价不能超过T, 问k最小是多少。

解法:

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个数,新的数继续丢进去

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std; 

#define LL long long
#define N 200100

LL a[N],T,n,t;
queue<int> q1, q2;

inline bool check(int x) {
    while(!q1.empty()) q1.pop();
    while(!q2.empty()) q2.pop();
    for(int i = 1 ; i <= n ; i++)
        q1.push(a[i]);
    int num = 0;
    LL sum = 0,ans = 0;
    if((n - 1) % (x - 1) != 0) {
        num = (n - 1) % (x - 1) + 1;
        for(int i = 1 ; i <= num ; i++) {
            sum += q1.front();
            q1.pop();
        }
        q2.push(sum);
        ans += sum;
    }
    while(!q1.empty()) {
        sum = 0;
        for(int i = 1 ; i <= x ; i++) {
            if(!q1.empty() && !q2.empty()) {
                if(q1.front() <= q2.front()) {
                    sum += q1.front();
                    q1.pop();
                } else {
                    sum += q2.front();
                    q2.pop();
                }
            } else if(q1.empty()){
                sum += q2.front();
                q2.pop();
            } else if(q2.empty()){
                sum += q1.front();
                q1.pop();
            }
        }
        ans += sum;
        q2.push(sum);
    }
    if(ans > T) return false;
    sum = num = 0;
    while(!q2.empty()) {
        sum += q2.front();
        q2.pop();
        num++;
        if(num == x) {
            q2.push(sum);
            ans += sum;
            sum = num = 0;
            if(q2.size() == 1) break;
        }
    }
    if(ans > T) return false;
    else return true;
}

int main() {
    scanf("%lld",&t);
    while(t--) {
        scanf("%lld%lld",&n,&T);
        for(int i = 1 ; i <= n ; i++)
            scanf("%lld",&a[i]);
        sort(a + 1, a + n + 1);
        LL l = 2, r = n, mid, ans = 0;
        while(l <= r) {
            mid = (l + r) >> 1;
            if(check(mid)) {
                ans = mid;
                r = mid - 1;
            } else l = mid + 1;
        }
        printf("%lld\n", ans);
    }
    //system("pause");
    return 0;
}

原文地址:https://www.cnblogs.com/Repulser/p/11396841.html

时间: 2024-11-05 19:00:03

Hdu 5884的相关文章

两个队列+k叉哈夫曼树 HDU 5884

1 // 两个队列+k叉哈夫曼树 HDU 5884 2 // camp题解: 3 // 题意:nn个有序序列的归并排序.每次可以选择不超过kk个序列进行合并,合并代价为这些序列的长度和.总的合并代价不能超过TT, 问kk最小是多少. 4 // . 5 // 题解:首先二分一下这个kk.然后在给定kk的情况下,这个代价其实就是kk叉的哈夫曼树问题.因此直接套用哈夫曼树的堆做法即可.复杂度O(nlog^2n) 6 // ?,这样优化一下读入是可以卡过去的. 7 // 然后主代码手表示,利用合并的单调

Sort HDU - 5884 哈夫曼权值O(n)

http://acm.hdu.edu.cn/showproblem.php?pid=5884 原来求一次哈夫曼可以有O(n)的做法. 具体是,用两个队列,一个保存原数组,一个保存k个节点合并的数值,然后每次选k个的时候,用two point在两个队列中选k个出来即可. 然后又把新的节点放去第二个队列那里去. 所以,首先需要排序(这样就和求 一次huffman编码的时间复杂度一样了nlogn) 然后这样明显第二个队列是单调递增的. #include <bits/stdc++.h> #define

HDU 5884 Sort (二分)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5884 nn个有序序列的归并排序.每次可以选择不超过kk个序列进行合并,合并代价为这些序列的长度和.总的合并代价不能超过TT, 问kk最小是多少 用一个队列维护合并的数,二分一下判断合理性.注意一点的是要是(n - 1)%(k - 1) != 0的话,就要先合并前(n - 1)%(k - 1) + 1项,这样会更优一点.还有细节问题很多要注意. 1 //#pragma comment(linker,

HDU 5884 Sort(二分+优先队列)

http://acm.hdu.edu.cn/showproblem.php?pid=5884 题意:有个屌丝设计了一个程序,每次可以将k个数组进行合并,代价为这k个数组总的长度之和.现在另外一个屌丝要他最后合并成一个数组时的总代价不能超过T.求k的最小值. 思路:贪心策略是长度越小的肯定先进行合并.一开始是直接用一个优先队列,但是这样会TLE的.. 后来改成了用两个队列进行模拟,先将数组排好序然后放入队列之中,每次合并之后的放入另一个队列之中,每次只需要再两个队列之中选择小的即可. 1 #inc

HDU - 5884 - Sort

原题链接 题意 给你n个序列以及序列内元素个数,现要求进行归并,花费为归并过程中序列长度的和,给定一个花费T,问最小的k(每次归并的最大序列个数)为多少. 分析 首先应该想到的是二分.然后思考如何check呢.排序,贪心的来,每次都选最小的前若干个.要注意的是,最后k-1个当然是在最后一次归并,那么要满足这样的情况,我们要判断(n-1)%(k-1)是否有剩余,有的话就先进行一次[余数+1]的归并,这样就保持了最优. #include <iostream> #include<cstdio&

HDU 5884 Sort (二分+k叉哈夫曼树)

题意:n 个有序序列的归并排序.每次可以选择不超过 k 个序列进行合并,合并代价为这些序列的长度和.总的合并代价不能超过T, 问 k最小是多少. 析:首先二分一下这个 k .然后在给定 k 的情况下,这个代价其实就是 k 叉的哈夫曼树问题.然后用两个队列维护一下就好. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string>

HDU 5884 Sort -2016 ICPC 青岛赛区网络赛

题目链接 #include <iostream> #include <math.h> #include <stdio.h> #include<algorithm> #include<cstring> #include<queue> using namespace std; int data[100005]; int main() { int T,n,t; scanf("%d",&T); while(T--)

HDU 5884 Sort ——(K叉哈夫曼树)

这题真心比较奥义,先见这个人的博客:http://blog.csdn.net/libin66/article/details/52565484 补0的方法是使得其满足成为满K叉树,而其博客中所说的“所以当(n-1)%(k-1)!=0的时候,会出现归并不能最大化个数的情况,这样会影响二分的单调性”我作如下的解释: 至于为什么不加0,sum会变大呢?作如下的解释:因为有一次合并不是最大个数的话,与其让它在后面单独合并增加权值还不如在前面补0合并呢,毕竟我们在算k的时候sum越小越好嘛~ 原先代码如下

HDU 5884 (贪心)

problem sort 题目大意 有n个数组,每个数组有a[i]个元素,每次可以将至多k个数组合并为一个数组,所花费代价为这些数组的元素和.给定代价上限,求将所有数组合并为1个数组的最小k. 解题分析 二分k后就成了k叉哈夫曼树问题. 对于k叉哈夫曼树,可以利用所合并元素的权值单调性,用两个双端队列来实现. 另外,如果(n-1)%(k-1) != 0 , 则需要另外在补上(k-1) - (n-1)%(k-1)个0. 参考程序 1 #include <map> 2 #include <s