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。N≤5000,1≤Si≤Ti≤108,1≤Vi≤108。
澄清:一个时刻只能做一件任务,做一个任务也只需要一个时刻。
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{posi−1+1,li},那么就可以处理出每一个点最靠左的不冲突的位置。那么我们每一个点所存放的位置一定是某一个posi(1≤i≤n)pos_i(1\le i \le n)posi(1≤i≤n)。
然后我们用类似于匈牙利算法的思路进行物品可位置的匹配即可。
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;
}