『贪心』BZOJ2034 [2009国家集训队]最大收益

本文介绍了一种基于贪心策略的任务调度算法,用于解决在有限时间内如何安排多个任务以获得最大收益的问题。通过对比任务的时间窗口和价值,确定最优的任务执行顺序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem\mathrm{Problem}Problem

给出 NNN 件单位时间任务,对于第 iii 件任务,如果要完成该任务,需要占用 [Si,Ti][S_i, T_i][Si,Ti] 间的某个时刻,且完成后会有 ViV_iVi 的收益。求最大收益。

N≤5000,1≤Si≤Ti≤108,1≤Vi≤108。N≤5000,1 ≤ S_i ≤ T_i ≤ 10^8,1 ≤ V_i ≤ 10^8。N50001SiTi1081Vi108

澄清:一个时刻只能做一件任务,做一个任务也只需要一个时刻。


Solution\mathrm{Solution}Solution

我们显然要将物品从大到小进行排序,对于当前物品 iii 来说:

①:如果左端点没有物品,肯定是要选择的。

②:如果左端点已经存在了物品,且物品的价值要大于viv_ivi

  • iii 的右端点较大,那么当前位置一定是保留原有物品更优,物品 iii 向后匹配。
  • iii 的右端点较小,那么有两种情况:
    • iii 代替了原来的物品,那么需要保证原来的物品在后面也要匹配到,这样在能保证物品 iii 的加入会使答案较优。
    • 若原来的物品到后面的位置没有地方存放了,那么显然由于 iii的右端点较小,也无法在后面存在。因此在这种情况下,当前物品不选择是较优的。

思考一下上述贪心的策略:我们每一次都是考虑当前的收益,显然只需要满足当前收益尽可能大就一定是最优解;假设当前物品本来可以放到某一个位置的但是没有放,那么它一定可以把后面放在本在这个位置的物品进行替换,一定会使答案更优。

考虑具体的实现:由于位置过大,我们需要对此进行离散化。

我们记posi=max⁡{posi−1+1,li}pos_i=\max\{pos_{i-1}+1,l_i\}posi=max{posi1+1,li},那么就可以处理出每一个点最靠左的不冲突的位置。那么我们每一个点所存放的位置一定是某一个posi(1≤i≤n)pos_i(1\le i \le n)posi(1in)

然后我们用类似于匈牙利算法的思路进行物品可位置的匹配即可。


Code\mathrm{Code}Code

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int N = 10000;

int n, res = 0;
int bel[N], pos[N]; 
map < int, int > tag;
struct node { int l, r, w, s; } a[N];

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (!isdigit(c)) w |= c == '-', c = getchar();
	while (isdigit(c)) s = s*10+c-48, c = getchar();
	return w ? -s : s;
}

bool Pig1(node p1, node p2) { return p1.l < p2.l; } 
bool Pig2(node p1, node p2) { return p1.w > p2.w; }

bool check(int k, int x) //尝试将物品k放在pos[x]的位置上 
{
	if (pos[x] > a[k].r) return 0;
	if (bel[x] == 0) return bel[x] = k, 1;
	if (a[k].r >= a[bel[x]].r) return check(k, x+1);
	if (check(bel[x], x+1)) return bel[x] = k, 1;
	return 0;
}

signed main(void)
{
	n = read();
	for (int i=1;i<=n;++i) {
		a[i].l = read();
		a[i].r = read();
		a[i].w = read(); 
	}
	sort(a+1, a+n+1, Pig1);
	for (int i=1;i<=n;++i) {
		pos[i] = max(pos[i-1]+1, a[i].l);
		tag[pos[i]] = i;
		a[i].s = tag[a[i].l];
	}
	sort(a+1, a+n+1, Pig2);
	for (int i=1;i<=n;++i)
		if (check(i, a[i].s)) res += a[i].w;
	cout << res << endl;
	return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值