CF1475-D. Cleaning the Phone

CF1475-D. Cleaning the Phone

题意:

手机上有很多应用非常占用内存,你要清理内存。对于每个应用 i i i有以下描述:应用 i i i占用了 a i a_i ai的空间,它的方便度为 b i b_i bi

现在让你删除其中部分应用使得删除的应用占用的空间总大小大于等于 m m m且损失的方便度最小。


思路:

按照方便度将应用分类,每一类按照应用占用的空间大小从大到小排序,之后将每一类应用占用空间的前缀和求出来。

依次枚举删除前 j j j个方便度为 1 1 1的应用,这时已经删除了的应用的总内存空间就是 p r e 1 [ j 1 ] pre1[j_1] pre1[j1],那么还要从方便度为 2 2 2的应用中移除 m − p r e 1 [ j 2 ] m-pre1[j_2] mpre1[j2]的空间,这时只需要用 l o w e r _ b o u n d lower\_bound lower_bound p r e 2 pre2 pre2中找到这个值就可以了,这里设这个位置为 j 2 j_2 j2。每次枚举,它损失的总方便度为 1 ∗ i + 2 ∗ j 1*i+2*j 1i+2j,在所用的情况中取最小的即可。


一些疑问:

1.为什么不都删除方便度为1的应用?

假如有四个应用,占用的空间分别为1,1,1,5,方便度分别为1,1,1,2,现在要m=3,观察一下就可以发现选那个方便度为 2 2 2的应用损失的方便度反而更小。

2.为什么要按照方便度划分成两组?

分类之后数据更好处理。如果不划分可以通过内存/方便度的比值进行排序,从前往后加。但这样会出现一个棘手的问题:你从前往后加,加到第 j j j个数字的时候总空间已经超过 m m m了,但是第 j j j个应用的方便度为 2 2 2,有没有发现问题?可能后面有一个应用,它的方便度为 1 1 1,虽让它的内存/方便度比第 j j j个应用小,但是它已经完全可以让前 j − 1 j-1 j1个应用的空间加上他的空间大小使得总大小大于等于 m m m,但是找这个数字很麻烦,而且会徒增复杂度。

上面复杂的描述已经可以说明问题,我们不想把简单的问题复杂化。


AC代码:

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

typedef long long ll;

const ll INF = 0x3f3f3f3f3f3f3f3f;
const int Maxn = 200005;

ll a[Maxn];
std::vector<ll>b1, b2;
std::vector<ll>pre1, pre2;

void solve() {
	int n;
	ll m, t, sum = 0;
	scanf("%d %lld", &n, &m);
	for (int i = 0; i < n; i++) {
		scanf("%lld", a + i);
		sum += a[i];
	}
	b1.clear();
	b2.clear();
	for (int i = 0; i < n; i++) {
		scanf("%lld", &t);
		if (t == 1) {
			b1.push_back(a[i]);
		} else {
			b2.push_back(a[i]);
		}
	}
	if (sum < m) {
		printf("-1\n");
		return;
	}
	std::sort(b1.begin(), b1.end(), std::greater<ll>());
	std::sort(b2.begin(), b2.end(), std::greater<ll>());

	pre1.clear(); pre1.push_back(0); // 这里是因为有可能方便度为1的一个也不选
	pre2.clear(); pre2.push_back(0); // 与上同理
	
	for (int i = 1; i <= b1.size(); i++) {
		t = pre1[i - 1] + b1[i - 1];
		pre1.push_back(t);
	}
	for (int i = 1; i <= b2.size(); i++) {
		t = pre2[i - 1] + b2[i - 1]; 
		pre2.push_back(t); 
	} 
	ll ans = INF; 
	for (int i = 0; i < pre1.size(); i++) { 
		ll tar = m - pre1[i];
		int p = std::lower_bound(pre2.begin(), pre2.end(), tar) - pre2.begin();
		if (p == pre2.size()) {
			continue;
		}
		ans = std::min(ans, (ll)(i + p * 2));
	}
	printf("%lld\n", ans);
}

int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值