# POJ1456_Supermarket
题意:
有 \(n\) 个商品,第 \(i\) 个商品利润 \(p[i]\) ,会在 \(d[i]\) 天过期(可以在第 \([1,\ d[i]]\) 天之间售出,不能在第 \(d[i]+1\) 天售出)
问可以获得的最大利润
解:
【并查集+贪心】:
【贪心】:
①:显然,优先考虑利润较大的商品如何售出
②:为了尽可能的包容其他商品,一个商品应该在过期之前尽可能晚地卖出
【并查集】:
首先将所有商品关于利润从大到小排序
从1到n依次考虑每个商品
根据上文分析,第 \(i\) 个商品最好能在 \(d_{i}\) 天卖出,但是如果发现第 \(d_{i}\) 天已经在之前“安排”上了要卖别的商品,
就依次考虑在第 \(d_{i}-1,\ d_{i}-2,\ ...\ 2,\ 1\) 天售出,如果找不到空位则不售出第 \(i\) 个商品
但是,暴力从 \(d_{i}\) 天开始往前枚举显然是不可取的
考虑这样一种解决方案
以每一天为元素做并查集,初始化 \(fa[x] = x\) , \(x\) 代表第 \(x\) 天
假设第1个商品在 \(d_{1}\) 天过期,查询 \(fa[d_{1}]=d_{1}\) ,就把第1个商品放在第 \(d_{1}\) 天卖出,同时合并第 \(d_{1}\) 天和第 \(d_{1}-1\) 天的集合
即令 \(fa[d_{1}]=d_{1}-1\)
假设第2个商品也恰在第 \(d_{1}\) 天过期,查询 \(fa[d_{1}]=d_{1}-1\) ,就把第2个商品放在第 \(d_{1}-1\) 天售出,同时令 \(fa[d_{1}-1]=d_{1}-2\)
若对于第 \(x\) 天的查询 \(fa[x]=0\) 证明第 \([1,\ x]\) 天中没有空位,此商品不再售出
以此类推,此过程类似于链表查询,顺次向前查找第一个空位,但由于并查集具有路径压缩算法,查询时间可降至常数级别
代码:
int n;
struct PRODUCT{
int p, d;
}a[10010];
int sale[10010], fa[10010];
int ans;
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
bool cmp(PRODUCT x, PRODUCT y) { return x.p > y.p; }
int main() {
while (cin >> n) {
memset(sale, 0, sizeof sale);
int maxd = 0;
for (int i = 1; i <= n; i++)
cin >> a[i].p >> a[i].d, maxd = max(maxd, a[i].d);
for (int i = 1; i <= maxd; i++) fa[i] = i;
sort(a + 1, a + 1 + n, cmp);
for (int i = 1; i <= n; i++) {
int rt = find(a[i].d);
if (rt == 0) continue;
sale[rt] = a[i].p;
fa[rt] = rt - 1;
}
ans = 0;
for (int i = 1; i <= maxd; i++) ans = ans + sale[i];
cout << ans << endl;
}
return 0;
}