HDU-5884 Sort

题目大意:

给你n个长度为ai的序列,让你把这n个序列合并,每次合并需要一个cost,值为合并的序列的长度之和,每次最多可以合并k个序列。现在问你,给出cost最大为T,最小的k是多少。

解题思路:

显然,可以用优先队列水一发,复杂度为nlognlogn

二分枚举k,然后每次取最小的k个加在一起,加入队列,然后判断,加和是否比T大。

其实就是一个k维哈夫曼树的优先队列优化,来篇博客,传送门

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <functional>
using namespace std;

typedef long long LL;

const int maxn = 100005;

int a[maxn];
int n, t, cas;

int Scan() {
	int res = 0, ch, flag = 0;
	if ((ch = getchar()) == '-')
		flag = 1;
	else if (ch >= '0'&&ch <= '9')
		res = ch - '0';
	while ((ch = getchar()) >= '0'&&ch <= '9')
		res = res * 10 + ch - '0';
	return flag ? -res : res;
}
void Out(int a) {
	if (a > 9)
		Out(a / 10);
	putchar(a % 10 + '0');
}
bool judge(int k) {
	LL sum = 0, pre = 0;
	int inx = 0, cnt = 0;
	priority_queue<LL, vector<LL>, greater<LL> > q;
	if ((n - 1) % (k - 1)) {
		for (int i = 0; i < ((k - 1) - (n - 1) % (k - 1)); ++i) q.push(0);
	}
	while (inx < n) {
		if (cnt < k) {
			if (!q.empty() && q.top() < a[inx]) {
				pre += q.top(); q.pop();
				++cnt;
			} else {
				pre += a[inx++];
				++cnt;
			}
		} else {
			cnt = 0;
			if (sum + pre > t) return false;
			sum += pre;
			q.push(pre); pre = 0;
		}
	}
	while (!q.empty()) {
		if (cnt < k) {
			pre += q.top(); q.pop();
			++cnt;
		} else {
			cnt = 0;
			sum += pre;
			if (sum > t) return false;
			q.push(pre); pre = 0;
		}
	}
	if (sum + pre > t) return false;
	return true;
}
int main() {
	cas = Scan();
	while (cas--) {
		n = Scan(); t = Scan();
		for (int i = 0; i < n; ++i)
			a[i] = Scan();
		sort(a, a + n);
		int l = 0, r = n, mid;
		while (l < r) {
			mid = (l + r) >> 1;
			if (judge(mid)) r = mid;
			else l = mid + 1;
		}
		Out(r); puts("");
	}
	return 0;
}


posted @ 2016-09-17 22:54  _Wilbert  阅读(134)  评论(0编辑  收藏  举报