描述
输入
第一行两个整数N。
接下来N行每行两个整数,分别表示每个商品的利润、过期时间。
1<=N,利润,时间<=10000。
输出
样例输出
算法正确性证明
Rainbow开了一家商店,在一次进货中获得了N个商品。
已知每个商品的利润和过期时间。
Rainbow每天只能卖一个商品,并且过期商品不能再卖。
Rainbow也可以选择在每天出售哪个商品,并且一定可以卖出。
由于这些限制,Rainbow需要制定一份合理的售卖计划。请你计算一下,Rainbow最终可以获得的最大收益。
接下来N行每行两个整数,分别表示每个商品的利润、过期时间。
1<=N,利润,时间<=10000。
输出一个整数,表示Rainbow最终可以获得的最大收益。
样例输入
7 20 1 2 1 10 3 100 2 8 2 5 20 50 10
185
一开始我的思路是:对每天将要过期的商品进行贪心,即每天都从当天将要过期的商品中选出价值最大的商品。这个思路显然是错的,提交上去之后,华丽丽的2分。仔细考虑一下,如果有两个价值100的商品都在第2天过期,一个价值10的商品在第一天过期,那么正确答案应当是选择两个100的商品,而不是第一天选择10,第二天选择100。
想通这一点之后,自然就明白该如何贪心了。正确的解法应当是:对商品的价值进行贪心,即尽量安排价值大的商品出售,最后再考虑价值小的。
那么如何“安排”商品的出售时间呢?
思路就是尽量在接近过期的时间出售该商品,这样就可以为过期时间早的商品留出选择的余地。
以测试样例为例,对7件商品进行降序排序:
价值:100 50 20 10 8 5 2
天数: 2 10 1 3 2 20 1
- 100->2(空),出售
- 50->10(空),出售
- 20->1(空),出售
- 10->3(空),出售
- 8->2(满)->1(满)->舍弃
- 5->20(空),出售
- 2->1(满)->舍弃
结果为 100 + 50 + 20 + 10 + 5 = 185
代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
struct product{ // product记录商品的价值和过期时间
int v;
int t;
}p[10005];
int visited[10005]; // 记录某天是否已经安排出售其他商品
bool mycmp( struct product& a, struct product& b ){ // 定义比较函数
return a.v > b.v || a.v == b.v && a.t < b.t;
}
int main(){
int n;
scanf("%d",&n);
for( int i = 0; i < n; ++i )
scanf("%d %d",&p[i].v,&p[i].t);
sort(p,p+n,mycmp); // 排序
int j = 0, ans = 0;
while( j < n ){ // 贪心
for( int i = p[j].t; i > 0 ; --i ){ // 向前遍历,直到找到可以安排出售该商品的时间
if( !visited[i] ){
ans += p[j].v;
visited[i] = 1;
break;
}
} // 否则,舍弃该商品
++j;
}
printf("%d",ans);
return 0;
}
算法正确性证明
1. 首先证明,存在一个最优解P,其出售的商品与算法的结果A选择的商品相同,经过有限次转换,可以令P安排出售商品的顺序与A相同。证明如下:
假设P中商品i在第Pi天出售,在A中i在第Ai天出售(由于算法的安排策略,有Ai>=Pi),Pi != Ai。交换P中分别位于Pi和Ai的两个商品i和j,i的价值一定比j大。对于i,调换到Ai<=Deadlinei出售不会过期,对于j,调换到Pi<=Ai的位置出售时间提前,也不会过期。所以这样的转换不改变最终出售商品的总价值。
2.