HDU - 5884 - Sort

原题链接

题意

给你n个序列以及序列内元素个数,现要求进行归并,花费为归并过程中序列长度的和,给定一个花费T,问最小的k(每次归并的最大序列个数)为多少。

分析

首先应该想到的是二分。然后思考如何check呢。排序,贪心的来,每次都选最小的前若干个。要注意的是,最后k-1个当然是在最后一次归并,那么要满足这样的情况,我们要判断(n-1)%(k-1)是否有剩余,有的话就先进行一次[余数+1]的归并,这样就保持了最优。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<cstdlib>
#include<set>
#include<string>

using namespace std;
const int maxn=1e5+10;
int t,n;
int a[maxn],sum[maxn];
priority_queue< int, vector<int> , greater<int>  > q;

bool check(int k){
    while( !q.empty() ) q.pop();
    int m=(n-1)%(k-1);
    int res=0;

    if(m){
        m++;
        res+=sum[m];
        q.push(res);
    }
    for(int i=m+1;i<=n;i++) q.push(a[i]);
    int up=(n-1)/(k-1);
    for(int i=0;i<up;i++){
        int tk=k,tt=0;
        while(tk--){
            tt+=q.top();
            q.pop();
        }
        res+=tt;
        q.push(tt);
    }
    if(res>t) return false;
    return true;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&t);
        sum[0]=0;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++)
            sum[i]=sum[i-1]+a[i];

        int l=2,r=n,mid;
        while(l<r){
            mid=(l+r)>>1;
            if(check(mid)) r=mid;
            else l=mid+1;
        }
        if(n<=1) r=1;
        cout<<r<<endl;
    }
    return 0;
}
时间: 2024-10-12 02:20:54

HDU - 5884 - Sort的相关文章

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 (二分+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 sort

这道题目,我做的时候不会哈夫曼树,自己贪心是错的都不知晓.还可以发现这里不必用优先队列,可以用两个队列,毕竟插入的数是有单调性的. 1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 using namespace std; 6 const int N=300010; 7 int a[N],n,S,f1,f2,b1,b2; 8 int

HDU - 5884 Sort (二分答案+贪心)

有n个数字,你需要把这n个数字合成一个数字,每次只能把k个数字合并成一个,花费为这k个数字的和. 给一个最大花费,问不超过这个最大花费的情况下,k的最小值. Sample Input 1 5 25 1 2 3 4 5 Sample Output 3 这个题很容易想到二分答案+优先队列check 然而这样复杂度是 O(n logn*logn ),会TLE(这特么都会TLE?加个读入优化就过了) 可以先给所有数字排个序,然后用两个队列,一个存原来的数字,一个存新合成的数字. 所以两个队列都是有序的.

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叉哈夫曼树 HDU 5884

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