CF1475-D. Cleaning the Phone

CF1475-D. Cleaning the Phone

题意:

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

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


思路:

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

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


一些疑问:

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

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

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

分类之后数据更好处理。如果不划分可以通过内存/方便度的比值进行排序,从前往后加。但这样会出现一个棘手的问题:你从前往后加,加到第\(j\)个数字的时候总空间已经超过\(m\)了,但是第\(j\)个应用的方便度为\(2\),有没有发现问题?可能后面有一个应用,它的方便度为\(1\),虽让它的内存/方便度比第\(j\)个应用小,但是它已经完全可以让前\(j-1\)个应用的空间加上他的空间大小使得总大小大于等于\(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;
}

posted @ 2021-01-29 13:08  牟翔宇  阅读(103)  评论(0编辑  收藏  举报