赛题-航运中心 (51nod.com)
题目描述
- 1.0 秒
- 262,144.0 KB
- 100 分
有 n 件行李(编号 1 到 n )以及 m 个箱子(编号 1 到 m )。第 i件行李的大小为 w[i] ,价值为 v[i] 。每个箱子只可以装一件行李,并且尺寸不能超过 x[i] 。
因为某种原因,箱子有时会变得不可用,有 q 个询问。每个查询给出两个整数 l和 r ,即从 l 到 r 号箱子不可用,请你回答,在这种情况下,剩余的箱子最多能够放下多少价值的行李。
收起
输入
第一行:3个数n,m,q,中间用空格分隔。
后面n行,每行2个数,对应n个箱子的 w[i] 和 v[i]。
之后一行共有m个数,表示箱子的容量。
之后q行,每行两个数 l,r 对应不可用箱子的范围。
其中1≤n,m,q≤50,1≤w[i],v[i],x[i]≤1000000。
输出
输出q行。每行一个询问的答案。
数据范围
对于100%的数据,
1≤n,m,q≤50; 1≤w[i],v[i],x[i]≤1000000; l≤r≤m。
输入样例
3 4 3
1 9
5 3
7 8
1 8 6 9
4 4
1 4
1 3
输出样例
20
0
9
解题思路:
这题看起来像是背包,实际上是贪心。我们对盒子排序后,从小到大逐个处理,对于每个盒子,让他拿到能装下的价值最高的行李。
由于盒子大小是有序的,因此对于后面的盒子,能装下的行李的数量是递增的。从小到大处理可以保证最终的方案最优。这样做的时间复杂度为 O ( m n q ) O(mnq) O(mnq) 。
如果用优先队列维护,则时间复杂度可以降为 O ( q m ( l o g ( n ) + l o g ( m ) ) ) O(qm(log(n)+log(m))) O(qm(log(n)+log(m))) 。其中 m l o g ( m ) mlog(m) mlog(m) 是对 m 个行李排序的复杂度,这部分还有优化的空间,我们可以通过一次排序,后面只是去掉范围在 (l,r) 的行李,这部分的平摊复杂度可以降为 O(m) ,总的时间复杂度为 $O(qm(log(n)))
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int n, m, q;
bool used_good[100];
bool used_box[100];
struct good
{
int v, w;
} a[100];
struct box
{
int id;// 盒子的id
int c; // 盒子的容量
} x[100];
// good 按照价值大的排前面。
bool cmp1(good a1, good a2)
{
return a1.v > a2.v;
}
// box 按照体积的从小到大排。
bool cmp2(box a, box b)
{
return a.c < b.c;
}
// 快读
inline int read()
{
int x = 0, f = 1;
char s = getchar();
while (s < '0' || s > '9')
{
if (s == '-')
f = -f;
s = getchar();
}
while (s >= '0' && s <= '9')
{
x = (x << 3) + (x << 1) + (s ^ 48);
s = getchar();
}
return x * f;
}
int main()
{
cin >> n >> m >> q;
for (int i = 1; i <= n; i++)
{
a[i].w = read();
a[i].v = read();
}
for (int i = 1; i <= m; i++)
{
x[i].c = read();
x[i].id = i; // box序号
}
// good 按照价值大的排前面。
sort(a + 1, a + n + 1, cmp1);
// box 按照体积的从小到大排。
sort(x + 1, x + m + 1, cmp2);
while (q--)
{
// q次问询,开始进行初始化
int l = read(), r = read();
int ans = 0;
memset(used_good, 0, sizeof used_good);
memset(used_box, 0, sizeof used_box);
// 从小到大遍历box,
for (int i = 1; i <= m; i++)
{
// 不能用的盒子跳过
if (l <= x[i].id && x[i].id <= r)
continue;
// 物品价值从大到小排列
for (int j = 1; j <= n; j++)
{
// 找到第一个能装的下的最价值的物品后跳出。
if (x[i].c >= a[j].w && !used_good[j] && !used_box[i])
{
ans += a[j].v;
used_good[j] = 1; // 标记物品使用过了。
used_box[i] = 1; // 标记盒子被使用了。
continue;
}
}
}
cout << ans << endl;
}
return 0;
}