pku 1456 Supermarket 贪心优先队列的应用 or 并查集
http://poj.org/problem?id=1456
题意:
给出n个折扣热卖的商品,给出每个产品的(pi,di)表示第i个热卖商品,卖出去可以获得利润pi,最晚期限是第di天。让你设计一个合理的出售列表,使获得的利润最多。
思路:
才开始完全理解错了题意,以为当出现最晚期限一样的时候只能买一种呢。直接排序做了。WA..后来看了discuss里面,原理理解错题意了。
首先要理解这种情况,如果有这样的数据 (2,1) (3,2),(4,2)我们要在第一天卖(3,2)第二天卖(4,2)才能得到最大的利润。那么如何求呢。我们利用优先队列来存储第几天卖那一种,那么这个优先队列的大小size就表示了我们前size天卖什么能获得最大利润,但出现后边的最后期限大于size时,直接放入队列即可,当出现相同的我们要替换掉至最小的那个,是的我们获得利润最大。
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define ll long long #define inf 0x7f7f7f7f #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll long long #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define N 10007 #define M 50007 using namespace std; struct node { int px,dx; }nd[N]; priority_queue<int,vector<int>,greater<int> >q; int n; int cmp(node a,node b) { if (a.dx != b.dx) return a.dx < b.dx; else return a.px > b.px; } int main() { // Read(); int i; while (~scanf("%d",&n)) { for (i = 0; i < n; ++i) { scanf("%d%d",&nd[i].px,&nd[i].dx); } sort(nd,nd + n,cmp); while (!q.empty()) q.pop(); for (i = 0; i < n; ++i) { // printf("%d %d\n",nd[i].dx,nd[i].px); if (nd[i].dx > q.size()) { q.push(nd[i].px); } else { int val = q.top(); if (val < nd[i].px)//将该中产品放在val所在的那一天出售 { q.pop(); q.push(nd[i].px); } } } int ans = 0; while (!q.empty()) { ans += q.top(); q.pop(); } printf("%d\n",ans); } return 0; }
并查集的方法确实不好想。
首先我们按贪心的思路来想的话,先把所有产品按照利润从大到小排序,然后这个把这个放在截止日期那天卖出,并做好标记,如果截至日期那天已经有其他产品占用了,那么可以把这个产品卖出的时间往前推,直到找到可以卖的那一天并标记好。 那么我们就可以利用并查集,i的根find(i)就表示小于等于i的期限可以放置商品的最靠近i的天。
#include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define ll long long #define inf 0x7f7f7f7f #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define ll long long #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define lowbit(x) (x)&(-x) #define Read() freopen("din.txt", "r", stdin) #define Write() freopen("dout.txt", "w", stdout); #define N 10007 #define M 50007 using namespace std; struct node { int px,dx; }nd[N]; int f[N]; int n; int cmp(node a,node b) { return a.px > b.px; } void init(int m) { for (int i = 0; i <= m; ++i) f[i] = i; } int find(int x) { if (f[x] != x) f[x] = find(f[x]); return f[x]; } int main() { // Read(); int i; int maxT; while (~scanf("%d",&n)) { maxT = 0; for (i = 0; i < n; ++i) { scanf("%d%d",&nd[i].px,&nd[i].dx); maxT = max(maxT,nd[i].dx); } sort(nd,nd + n,cmp); init(maxT); int ans = 0; for (i = 0; i < n; ++i) { int mk = find(nd[i].dx); if (mk > 0) { ans += nd[i].px; f[mk] = mk - 1; } } printf("%d\n",ans); } return 0; }