题目大意:
现有一批货物需要贩卖,货物总共有n件(0 ≤ n ≤ 10,000),每件货物都有一个利润pi(1 ≤ pi ≤ 10,000)和截止日期di(1 ≤ di ≤ 10,000),货物必须在截止时间点之前贩卖掉,否则就无法贩卖,时间是从第0天开始算的,一天只能卖一件货物,比如某货物截止日期为3,则最早可以在第0天开始贩卖,最晚必须在第2天开始贩卖,贩卖一件货物需要一天的时间。
现有多个测例(以EOF结束),每个测例都会给出n,以及每件货物的pi和di,要求确定一个最佳的贩卖顺序,使得最后获得的利润最大,要求输出该最大利润。
注释代码:
/*
* Problem ID : POJ 1456 Supermarket
* Author : Lirx.t.Una
* Language : C++
* Run Time : 63 ms
* Run Memory : 212 KB
*/
//思路:
//先贪心将所有货物按照利润从大到小排列
//之后逐个检查货物,将货物安排在里截止日期最近的那个时间点贩卖
//假设,刚开始时第一个被检查的货物的截止日期为5,则在4贩卖
//第二个备查的货物截止日期也为5,由于4已经被占用,则在3贩卖
//即一次向前一步查询,是否有空出来的日期,如果一直查到-1,则表示
//之前所有空闲时间都被占用,因此就不能被贩卖
//而为了加速这种查询(如果从截止日期一个一个向前查询空闲时间将会非常耗时)
//这里使用并查集进行优化
//将每个时间点作为集合中的元素,一段连续的时间可以并成一个集合
//集合的根结点就是时间点最空前的空闲的时间点
//比如截止时间序列为5 5 3 4 1 7
//则一开始find(5 - 1) = 4,因此4被占用了,因此需要将4和4 - 1所在集合合并得到根结点为3
//因此3就是这个集合中未被占用的最早时间点
//接下来是find(5 - 1) = 3,因此3被占用了,再将3的集合和3 - 1的集合合并得根结点2,因此
//2的集合(包括2、3、4)2是最早的没被占用的时间点
//接下来是find(3 - 1) = 2,因此2被占用,再将2和2 - 1合并,得根1
//再来find(4 - 1) = 1,因此1被占用,再将1和1 - 1合并,得到根0
//再来find(1 - 1) = 0,因此0被占用,再将0和0 - 1合并,得根-1,到此为止从0 ~ 4的时间都被占用了
//如果接下来遇到≤5的截止日期则都不能被贩卖了
//最后是find(7 - 1) = 6,被占用,6再和6 - 1合并,得到根5
//最后是0 1 2 3 4 6被占用
#include <algorithm>
#include <iostream>
#include <cstdio>
//最大货物数
#define MAXN 10000
//最大截止日期
#define MAXD 10000
using namespace std;
struct Prod {//Product,货物结构体
short p;//profit,利润
short d;//deadline,截止日期
bool//贪心,按利润从大到小排序
operator<(const Prod &oth)
const {
return p > oth.p;
}
};
Prod prd[MAXN];//product,货物
short fath[MAXD + 1];//并查集
int
find(int x) {//如果为-1则直接返回-1,表示已经无空闲时间可以被占用了
return x < 0 || x == fath[x] ? x : ( fath[x] = find( fath[x] ) );
}
bool
check(int d) {//检查截止日期为d的货物能否贩卖
int dd;//最左空闲时间点
dd = find( d - 1 );
if ( dd < 0 ) return false;//表示无空闲时间点可以被占用了
//可以被占用,则再和左边一个时间点所在集合合并
fath[dd] = find( dd - 1 );//根永远是最左未被占用时间点,所以经dd接在find( dd - 1 )上
return true;
}
int
max( int a, int b ) {
return a > b ? a : b;
}
int
main() {
int n;//货物数
int i;//计数变量
int maxd;//maximum deadline,记录最大的时间点,也是最大的截止日期时间点
int ans;//最终的最大利润
while ( ~scanf("%d", &n) ) {
maxd = 0;
for ( i = 0; i < n; i++ ) {
scanf("%d%d", &prd[i].p, &prd[i].d);
maxd = max( maxd, prd[i].d );
}
for ( i = 0; i <= maxd; i++ ) fath[i] = i;
sort(prd, prd + n);
ans = 0;
for ( i = 0; i < n; i++ )
if ( check( prd[i].d ) )
ans += prd[i].p;
printf("%d\n", ans);
}
return 0;
}
无注释代码:
#include <algorithm>
#include <iostream>
#include <cstdio>
#define MAXN 10000
#define MAXD 10000
using namespace std;
struct Prod {
short p;
short d;
bool
operator<(const Prod &oth)
const {
return p > oth.p;
}
};
Prod prd[MAXN];
short fath[MAXD + 1];
int
find(int x) {
return x < 0 || x == fath[x] ? x : ( fath[x] = find( fath[x] ) );
}
bool
check(int d) {
int dd;
dd = find( d - 1 );
if ( dd < 0 ) return false;
fath[dd] = find( dd - 1 );
return true;
}
int
max( int a, int b ) {
return a > b ? a : b;
}
int
main() {
int n;
int i;
int maxd;
int ans;
while ( ~scanf("%d", &n) ) {
maxd = 0;
for ( i = 0; i < n; i++ ) {
scanf("%d%d", &prd[i].p, &prd[i].d);
maxd = max( maxd, prd[i].d );
}
for ( i = 0; i <= maxd; i++ ) fath[i] = i;
sort(prd, prd + n);
ans = 0;
for ( i = 0; i < n; i++ )
if ( check( prd[i].d ) )
ans += prd[i].p;
printf("%d\n", ans);
}
return 0;
}
单词解释:
designate:vt, 指定,标出了
expire:vi, 期满,终止
optimal:adj, 最佳的,最理想的