题目大意:
给你n个商品:
每个商品有两个属性:
1,价值;2,最晚销售天数
且:每一天只可以买一件商品
求:可以获得的最大价值;
题目分析:
可以把这道题理解成了一道贪心的题目;
从这些商品里获得最大的价值;
既然是贪心,那么我的决策是什么:
尽量把价格高的卖出去
问题来了,在什么时候卖这些商品呢?
假如商品时间为n,那么销售的时间的区间是【1,n】,
最优的时间是什么呢?
当然是当天销售,这样对于同一天的商品来说,对价格排序后,肯定是价格高的先卖完。
当是如果这一天被占用了呢?
可以在【1,n-1】天里,找到一个距离n天最近的没有被占的时间,去销售;
那为什么不从前往后找呢?在第i天找,影响的是在【i,max】天销售的商品,i越大,影响的越小;
//暴力寻找[1,d-1]天内没被占用的天数;
还可以线段树查询;
//线段树维护距离n点最近的一个没被占的点的信息;
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int num=20012;
struct node
{
int d,v;
}a[num];
bool vis[num];
bool cmp(node c,node b)
{
return c.v > b.v;
}
//维护的是一棵树,k记录的这个区间最右边的非0的位置;
struct point
{
int L,R,s;
}e[num<<2];
void update(int k)
{
e[k].s=max(e[k<<1].s,e[k<<1|1].s);
return ;
}
void build(int k,int L,int R)
{
e[k].L=L;e[k].R=R;
if(L==R) {
e[k].s=R;
return ;
}
int mid=(e[k].L+e[k].R)>>1;
build(k<<1,L,mid);
build(k<<1|1,mid+1,R);
update(k);
return;
}
void change(int k,int L,int R)
{
if(e[k].R==R&&e[k].L==L)
{
e[k].s=0;
return ;
}
int mid=(e[k].L+e[k].R)>>1;
if(L<=mid) change(k<<1,L,R);
if(R>mid) change(k<<1|1,L,R);
update(k);
return ;
}
int search(int k,int L,int R)
{
if(e[k].L>=L&&e[k].R<=R)
{
return e[k].s;
}
int mid=(e[k].L+e[k].R)>>1;
int ans=0;
if(L<=mid) ans=max(search(k<<1,L,R),ans);
if(R>mid) ans=max(search(k<<1|1,L,R),ans);
return ans;
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
if(n==0){
printf("0\n");
continue;
}
memset(vis,0,sizeof(vis));
int mx=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i].v,&a[i].d);
mx=max(mx,a[i].d);
}
sort(a+1,a+n+1,cmp);
int ans=0;
build(1,1,mx);
for(int i=1;i<=n;i++)
{
if(!vis[a[i].d]){
vis[a[i].d]=1;
change(1,a[i].d,a[i].d);
ans+=a[i].v;
}
else
{
if(a[i].d==1) continue;//[1,n]是按照价格的大小排序的
int pos=search(1,1,a[i].d);
if(pos>0)
{
change(1,pos,pos);
vis[pos]=1;
ans+=a[i].v;
}
/*
for(int j=a[i].d-1;j>=1;j--)
{
if(!vis[j])
{
vis[j]=1;
ans+=a[i].v;
break;
}
}
*/
}
}
printf("%d\n",ans);
}
return 0;
}