POJ 1456 Supermarket

题目大意:

        现有一批货物需要贩卖,货物总共有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, 最佳的,最理想的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值