题目链接:[POJ 1456]Supermarket[贪心][优先队列or并查集优化]
题意分析:
给出若干个商品,每个都有自己的销售截至日期,每天只能销售一个,每销售一个都能得到钱,问:最多能挣到多少钱?
解题思路:
这里可以使用贪心的思路。从末尾往前面数日期,如果当天有商品截止,就把这些商品全都放入优先队列,然后卖出价值最大的那一个;如果当天没有商品截止,就去卖优先队列里价值最大的那个。具体证明大概就是:每天可以决策的商品都是可达的,既然为了最总利润最大,那么就卖出最大的。(嗯,很是勉强的证明啊XD)。
最后说说并查集优化:首先还是对商品按价值进行排序,每次取出价值最大的商品,判断是否能有日期能够给其卖出去,每次默认用最大的日期去卖,如果这个日期已经被使用了,就向前挪一天,直到不能挪,那这个商品就不卖了。
个人感受:
虽说是并查集专题,真心不知道哪里能用并查集。然后就给贪心过了。96MS。然后上网查查,哇,还真能用并查集,原来是优化,有了贪心的思路,整个并查集也好理解了,并查集还能这么用ORZ。
具体代码如下:
优先队列君(94ms):
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN = 1e4 + 111;
vector<int> v[MAXN]; // 存一下这一天都有什么商品截止了
int main()
{
int n, p, d, up;
while (~scanf("%d", &n))
{
up = 0;
for (int i = 1; i <= 1e4 + 11; ++i) v[i].clear();
for (int i = 0; i < n; ++i)
{
scanf("%d%d", &p, &d);
up = max(up, d);
v[d].push_back(p);
}
int ans = 0;
vector<int>::iterator it;
priority_queue<int> pq;
for (int i = up; i >= 1; --i)
{
if (v[i].size())
{
for (it = v[i].begin(); it != v[i].end(); ++it) // 将所有商品放入优先队列
pq.push(*it);
ans += pq.top();
pq.pop();
}
else if (pq.size())
{
ans += pq.top();
pq.pop();
}
}
printf("%d\n", ans);
}
return 0;
}
并查集优化(63ms):
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN = 1e4 + 111;
int p[MAXN];
struct N{
int p, d;
bool operator < (const N& t)const
{
return p > t.p;
}
}a[MAXN];
int find(int x)
{
return p[x] == x ? x : p[x] = find(p[x]);
}
int main()
{
int n;
while (~scanf("%d", &n))
{
int ans = 0;
for (int i = 0; i <= 1e4 + 11; ++i) p[i] = i;
for (int i = 0; i < n; ++i)
{
scanf("%d%d", &a[i].p, &a[i].d);
}
sort(a, a + n);
for (int i = 0; i < n; ++i)
{
int temp = find(a[i].d); // 查找可用的日子
if (temp > 0)
{
p[temp] = temp - 1; // 使用之后将日期向前挪
ans += a[i].p;
}
}
printf("%d\n", ans);
}
return 0;
}