# 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;
}

posted @ 2020-07-26 11:15  熹圜  阅读(101)  评论(0编辑  收藏  举报