method 1:优先队列(47ms)
毕竟看起来就像优先队列的题目,第一反映就是沿着时间线反向看,将所有的截止日期大于等于第 i 天的商品全都push进优先队列,然后将堆顶(价值最大的)弹出,就是在这一天要出售的商品。下面是AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 10001
using namespace std;
struct products{
int val, ddl;
bool operator < (const products& a) const // 在优先队列里的排序依据是价值大小
{
return val < a.val;
}
};
int n;
bool cmp(const products& a, const products& b) // 注意加const关键字(之前没有加,被判成ce)
{
return a.ddl > b.ddl;
}
int main()
{
while(~scanf("%d", &n))
{
products pro[N]; // 开始把它定义成全局数组了,不知道是哪里没有清零的问题答案不对,这样就没问题了
int res = 0;
priority_queue<products> que;
int ed = -1;
for (int i = 0; i < n; i ++)
{
scanf("%d%d", &pro[i].val, &pro[i].ddl);
ed = max(ed, pro[i].ddl); // 找到最大的截止日期
}
sort(pro, pro + n, cmp); // 先sort一遍可以简省下面一片代码的id(商品的序号)和ava(是否待售available)的空间
int last = 0;
for (int i = ed; i >= 1; i --) // 从最大的截止日期开始往回看
{
for (int j = last; j < n && pro[j].ddl >= i; j ++) // 将所有的满足ddl >= i的商品全都push进去
{
que.push(pro[j]);
last = j + 1; // 用一个last来标记上次取到了第几个,下次从第几个开始push(防止重复push)
}
if(que.size())
{
products t = que.top();
que.pop();
res += t.val;
}
}
printf("%d\n", res);
}
return 0;
}
下面是两个mle的代码,每件商品的结构体还额外定义了id(序号)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define N 10001
using namespace std;
struct products{
int val, ddl;
int id; // 没有sort,每次用id查询原来的位置,在销售之后,把该商品的val标记成-1
bool operator < (const products& a) const
{
return val < a.val;
}
};
int n;
int main()
{
while(~scanf("%d", &n))
{
products pro[N];
int res = 0;
priority_queue<products> que;
int ed = -1;
for (int i = 0; i < n; i ++)
{
scanf("%d%d", &pro[i].val, &pro[i].ddl);
ed = max(ed, pro[i].ddl);
pro[i].id = i;
}
for (int i = ed; i >= 1; i --)
{
for (int j = 0; j < n; j ++) //可以发现不预先sort一遍,连循环的时间复杂度都要高些
{
if(pro[j].ddl >= i && pro[j].val != -1) //找到一件满足时间要求并且待售的商品
{
que.push(pro[j]);
}
}
if(que.size())
{
products t = que.top();
que.pop();
res += t.val;
pro[t.id].val = -1;
}
}
printf("%d\n", res);
}
return 0;
}
下面这个是最原始的,还多开了一个ava布尔数组(ava = true 表示待售)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define N 10010
using namespace std;
struct products{
int val, ddl;
bool ava;
int id;
bool operator < (const products& a) const
{
return val < a.val;
}
products(): ava(true){}
};
int n;
int main()
{
while(~scanf("%d", &n))
{
products pro[N];
int res = 0;
priority_queue<products> que;
int ed = -1;
for (int i = 0; i < n; i ++)
{
scanf("%d%d", &pro[i].val, &pro[i].ddl);
ed = max(ed, pro[i].ddl);
pro[i].id = i;
}
for (int i = ed; i >= 1; i --)
{
for (int j = 0; j < n; j ++)
{
if(pro[j].ddl >= i && pro[j].ava)
{
que.push(pro[j]);
}
}
if(que.size())
{
products t = que.top();
que.pop();
res += t.val;
pro[t.id].ava = false;
}
}
printf("%d\n", res);
}
return 0;
}
2. 贪心 + 并查集优化(47ms)
- 先把商品按照价值由大到小排列一遍
- 开一个数组 f [ N ],f [ i ] 表示第 i 天的祖先(就是并查集中常开的那个记录父亲的数组),首先将每一天的父亲都指向自己
- 从头开始遍历每一个物品你,每次遍历到第 i 个物品,查找该物品的截止日期的根节点 fd,如果大于0,就说明这个物品可以出售,并且就在 fd 这一天出售,否则说明这件物品不能出售。因为商品是按价值优先排序的,价值大的商品应该选择在截止日期 ddl 出售,然后 ddl 就被占用了,就将 ddl 的根节点指向 ddl - 1,所有之后遍历的商品如果满足截止日期是 ddl,就应该在 ddl - 1 (现在的根节点)这一天及之前出售。(看一下图吧,在代码下面🙃)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 10001
using namespace std;
struct products{
int val, ddl;
bool operator < (const products& a) const
{
return val > a.val;
}
};
int n;
int f[N];
int find(int s)
{
if(s != f[s]) f[s] = find(f[s]);
return f[s];
}
int main()
{
while(~scanf("%d", &n))
{
products pro[N];
priority_queue<products> que;
int ed = -1;
for (int i = 0; i < n; i ++)
{
scanf("%d%d", &pro[i].val, &pro[i].ddl);
ed = max(ed, pro[i].ddl);
}
sort(pro, pro + n);
for (int i = 0; i <= ed; i ++) f[i] = i;
int res = 0;
for (int i = 0; i < n; i ++)
{
int fd = find(pro[i].ddl);
if(fd > 0)
{
res += pro[i].val;
f[fd] = fd - 1; // 把这一天的指向前一天
}
}
printf("%d\n", res);
}
return 0;
}