HDU - 5884 Sort 二分+尺取

题目链接:https://vjudge.net/problem/HDU-5884

题意:有n个数,每个数有个值,现在你可以选择每次K个数合并,合并的消耗为这K个数的权值和,问在合并为只有1个数的时候,总消耗不超过T的情况下,最小的K是多少

题解:首先二分肯定容易想到,然后再就是优先队列合并操作一下,这样n*log(n)*log(n),就超时了,所以我们怎么能用一种O(n)的方法呢,想一下,第二次合并得到的数一定比第一次合并得到的数大,所以我们就把合并的数单独放在一起,新合并的直接放到后面,两个序列直接尺取就ok了。


#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n;
ll m;
ll a[N];
queue<ll> q;
bool judge(ll x) {
//	cout<<x<<":"<<endl;
	while(!q.empty()) q.pop();
	ll res = 0, cnt = 0;
	ll len = n, num;
	int i = 1;
	if((n - 1) % (x - 1) != 0) {
		cnt = (n - 1) % ( x - 1);
		for(i = 1; i <= cnt + 1; i++)
			res+=a[i];
		q.push(res);
	}
	
	len = n - cnt;
	while(len > 1) {
		cnt = 0;
		num = 0;
		while(num < x && i <= n && !q.empty()) {
			if(a[i] > q.front()) {
				cnt += q.front();
				q.pop();
			} else {
				cnt += a[i];
				i++;
			}
			num++;
		}
		while(num < x && i <= n) {
			cnt += a[i];
			i++;
			num++;
		}
		while(num < x && !q.empty()) {
			cnt += q.front();
			q.pop();
			num++;
		}
		len -= num;
		res += cnt;
		
		q.push(cnt);
		len++;
		if(res > m) return 0;
	}
	return 1;
}
int main() {
	int T;
	ll l, r, mid;
	ll ans;
	scanf("%d", &T);
	while(T--) {
		
		scanf("%d %lld", &n, &m);
		for(int i = 1; i <= n; i++) {
			scanf("%lld", &a[i]);
		}
		sort(a + 1, a + 1 + n);
		l = 2, r = n;
		while(l <= r) {
			mid = (r + l) >> 1;
			if(judge(mid)) {
				r = mid - 1;
				ans = mid;
			} else {
				
				l = mid + 1;
			}
		}
		printf("%lld\n", ans);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值