题意
传送门 HDU 4864
题解
目标是最大化完成的任务数量,多解情况下最大化收益。
机器
(
x
u
,
y
u
)
(x_u,y_u)
(xu,yu) 能完成某个任务
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi),需要满足
{
x
i
≤
x
u
y
i
≤
y
u
\begin{cases} x_i\leq x_u\\ y_i\leq y_u\\ \end{cases}
{xi≤xuyi≤yu 那么最大化完成的任务数量,可以使用贪心策略。即将任务分别以
x
i
,
y
i
x_i,y_i
xi,yi 为第一、第二关键字排序,降序排序,顺序扫描任务,每次将任务分配给满足条件且第二关键字
y
u
y_u
yu 最小的机器。
简单证明,考虑策略的作用范围拓展到后续其他任务产生的影响,对于后续任务 j j j,都满足第一关键字 x j ≤ x i x_j\leq x_i xj≤xi;即是说,当前任务可指派的机器,对于后续的任务在第一关键字上也满足条件;设当前任务可指派的任意 2 2 2 台机器 u , v u,v u,v 满足 y v ≤ y u y_v\leq y_u yv≤yu,那么后续任务只可能出现“ u , v u,v u,v 都能指派”,“ u , v u,v u,v 都不能指派”或者“ u u u 能指派, v v v 不能指派”三种情况之一,因此当前任务指派给第二关键字 y v y_v yv 较小的机器,对于整体问题的影响显然比选择第二关键字 y u y_u yu 较大的更好;考虑不完成当前任务的情况,由于每个任务对总数量的贡献至多是 1 1 1,故将它指派的机器分配给其他任务,对答案的贡献也不会更大。
对于每一个任务,完成获得的收益为 500 × x i + 2 × y i 500\times x_i+2\times y_i 500×xi+2×yi,可以观察到 2 × m a x y = 200 < 500 2\times maxy=200<500 2×maxy=200<500。那么以 x i x_i xi 为第一关键字, y i y_i yi 为第二关键字排序,顺序扫描可以保证优先处理可获得收益更多的任务,这正好匹配前述的贪心策略,那么在顺序处理任务,记录 y y y 的值域上可使用的机器数量,同时统计答案即可。
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 100005
#define maxy 105
typedef long long ll;
struct node
{
int x, y;
bool operator<(const node &b) const
{
if (x != b.x)
return x > b.x;
return y > b.y;
}
} mach[maxn], task[maxn];
int N, M, Y, cnt[maxy];
int main()
{
while (~scanf("%d%d", &N, &M))
{
Y = 0;
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < N; ++i)
scanf("%d%d", &mach[i].x, &mach[i].y), Y = max(Y, mach[i].y);
for (int i = 0; i < M; ++i)
scanf("%d%d", &task[i].x, &task[i].y);
sort(mach, mach + N);
sort(task, task + M);
ll num = 0, mon = 0;
for (int i = 0, j = 0; i < M; ++i)
{
while (j < N && mach[j].x >= task[i].x)
++cnt[mach[j++].y];
for (int y = task[i].y; y <= Y; ++y)
{
if (cnt[y])
{
--cnt[y], ++num, mon += task[i].x * 500 + task[i].y * 2;
break;
}
}
}
printf("%lld %lld\n", num, mon);
}
return 0;
}