我是靠谱客的博主 喜悦蜜蜂,最近开发中收集的这篇文章主要介绍51nod 1163 最高的奖励,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

题目链接

贪心,优先队列。

题意:
给了 n n n个任务的结束时间和奖励,让我们安排做任务的顺序,使最终能够得到的奖励最大。

题解:

朴素做法:
首先将所有的任务根据奖励的大小从大到小排序,如果奖励大小相同,结束时间晚的排在前面。贪心的策略就是,将该任务结束时间前第一个没有被安排任务的时间分给该任务。由于结束时间的数据较大,直接贪心会出错,我们发现,总共只有 n n n个任务,也就是最多只需要 n n n个单位的时间就可以做完所有任务。因此,我们在贪心时,将当前任务的结束时间与 n n n取一个 m i n min min

朴素版代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e4 + 10;
bool flag[MAXN];
struct P {
	ll e, w;
}p[50010];
bool cmp(P a, P b) {
	if (a.w == b.w) {
		return a.e > b.e;
	}
	return a.w > b.w;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	ll n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> p[i].e >> p[i].w;
	}
	sort(p, p + n, cmp);
	ll ans = 0;
	for (int i = 0; i < n; i++) {
		ll pos = p[i].e;
		pos = min(pos, n); // 最多用n个单位时间
		while (pos && flag[pos]) pos--; // 找到结束时间之前第一个没有任务的时间
		if (pos) {
			flag[pos] = true;
			ans += p[i].w;
		}
	}
	cout << ans << endl;
	return 0;
}

优化一点的版本是贪心+优先队列。

首先将所有的任务按照结束时间从早到晚排序。然后判断每个任务是否插入优先队列当中。如果当前任务的结束时间晚于队列的长度,也就是已经耗费的时间。那么将该任务的奖励直接加入队列,同时答案加上该任务奖励。如果,早于当前队列的长度,那么我们先将该任务加入队列,然后将队列当中奖励最少的任务拿出。

这样最终队列当中的任务奖励总和就是答案。时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

优化版代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e4 + 10;
bool flag[MAXN];
priority_queue<int, vector<int>, greater<int> > q;
struct P {
	ll e, w;
}p[50010];
bool cmp(P a, P b) {
	return a.e < b.e;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	ll n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> p[i].e >> p[i].w;
	}
	sort(p, p + n, cmp);
	ll ans = 0;
	for (int i = 0; i < n; i++) {
		if (p[i].e > q.size()) { // 晚于已经花费的时间,直接加入队列
			ans += p[i].w;
			q.push(p[i].w);
		}
		else { // 早于已花费时间,替换队列中奖励最少的任务
			ans += p[i].w;
			q.push(p[i].w);
			ans -= q.top();
			q.pop();
		}
	}
	cout << ans << endl;
	return 0;
}

并查集版本:

先将所有的任务按照奖励降序排序,然后使用并查集依次判断是否有当前任务可用的时间点。
我们使用并查集维护当前未使用的时间点,其中 f [ x ] = a f[x]=a f[x]=a表示结束时间为 x x x的任务可以在时间点 a a a完成。

时间复杂度是 O ( N a ( N ) ) O(Na(N)) O(Na(N)) a ( N ) a(N) a(N)是并查集的查找过程,接近于1。

并查集版代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e4 + 10;
int f[MAXN];
struct P {
	ll e, w;
}p[50010];
bool cmp(P a, P b) {
	return a.w > b.w;
}
int find(int x) {
	if (x <= 0) return -1;
	if (f[x] == x) {
		return f[x] = x - 1; // 当前时间点可用
	}
	else {
		return f[x] = find(f[x]);
	}
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	ll n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> p[i].e >> p[i].w;
		f[i] = i;
	}
	sort(p, p + n, cmp);
	ll ans = 0;
	for (int i = 0; i < n; i++) {
		if (find(p[i].e) >= 0) { // 查找是否存在可用的时间点
			ans += p[i].w;
		}
	}
	cout << ans << endl;
	return 0;
}

最后

以上就是喜悦蜜蜂为你收集整理的51nod 1163 最高的奖励的全部内容,希望文章能够帮你解决51nod 1163 最高的奖励所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部