题目概述
有N个商品,在其保质期dl天内卖掉可得利润v,否则没有利润,卖掉每个商品需要1天,求最大利润
时限
2000ms/6000ms
输入
每组数据第一个整数N,其后N对整数v,dl,输入中空白符会任意出现,输入到EOF为止
限制
1<=N,dl,v<=10000
输出
每行一个数,为所求最大利润
样例输入
4
50 2
10 1
20 2
30 1
7
20 1
2 1
10 3
100 2
8 2
5 20
50 10
3
1 1
100 2
100 2
2
99 1
100 100
样例输出
80
185
200
199
讨论
贪心,对于那些贵的商品,都希望其能在保质期内尽量晚的卖掉,但是无法避免有些由于太便宜而被扔掉,那么每次取最贵的,从保质期这天开始(当然也包括这一天)向前找空闲的一天,将之安排在这一天卖掉,由于已经按利润降序排,故后安排的肯定不如先安排的贵,因而若没找到空闲时间就直接扔掉了,然而从实现层面上,由于需要处理if语句,导致效率受到一定影响,采用优化后的并查集可以进一步加快速度
并查集,基本思想也是继承上面的,但是以并查集表示每一天,如果这天已经安排了,就以前一天为父节点,这样查找时总能找到空闲的一天,如果找到的是第0天,扔掉,这样就避开了很多if语句的处理,从实现层面上,初始化并查集时也不用全初始化,只要把保质期最长的那一天及之前都初始化就可以了
另外uva上也有这个题,但是下面两种方法都会超时
题解装填
贪心:284K,141MS,C++,723B
并查集:284K,63MS,C++,875B
题解代码
贪心:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 10004
#define memset0(a) memset(a,0,sizeof(a))
struct It//每个商品的结构
{
int v, dl;//value 利润 deadline 保质期
bool operator<(const It &b)const
{
return v > b.v;
}
}its[MAXN];
int N;//商品总数
int schedule[MAXN];//每天安排销售的商品的利润
int fun()
{
for (int p = 0; p < N; p++)
scanf("%d%d", &its[p].v, &its[p].dl);//input
sort(its, its + N);
int sum = 0;
for (int p = 0; p < N; p++)
for (int i = its[p].dl; i; i--)//从保质期最后一天枚举
if (!schedule[i]) {//找到空白的一天 题目保证每个商品利润都是正
sum += schedule[i] = its[p].v;
break;
}
return sum;
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
while (~scanf("%d", &N)) {//input
printf("%d\n", fun());//output
memset0(schedule);
}
}
并查集:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 10004
#define memset0(a) memset(a,0,sizeof(a))
struct It
{
int v, dl;
bool operator<(const It &b)const
{
return v > b.v;
}
}its[MAXN];
int N;
int id[MAXN];//父节点
int UFfind(int a)
{
int p = a;
while (a != id[a])
a = id[a];
return id[p] = a;
}
int fun()
{
int longest = -INF;//最长的保质期
for (int p = 0; p < N; p++) {
scanf("%d%d", &its[p].v, &its[p].dl);//input
longest = max(longest, its[p].dl);
}
sort(its, its + N);
for (int p = 0; p <= longest; p++)//初始化并查集
id[p] = p;
int sum = 0, available;//总利润 空闲的一天
for (int p = 0; p < N; p++)
if (available = UFfind(its[p].dl)) {//找到的是第0天就扔掉了
sum += its[p].v;
id[available]--;//这天已被占用 连到前一天
}
return sum;
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
while (~scanf("%d", &N))//input
printf("%d\n", fun());//output
}
EOF