UVA 12018 Juice Extractor(树状数组+dp)

题目:Juice Extractor

题意:水果忍者。。。给定N个水果各自的出现时间和消失时间,主角可以在这段时间内砍掉。如果一次性砍掉的水果数超过2,就可以得到和砍掉的水果数相同的分数。而主角有强迫症,如果他决定砍了,他就会把当前出现的水果全部砍掉,不会留到后面。

现在要问最多能获得多少分。

由于每次砍了之后,当前出现的水果就全部消失了,我们可以用dp[i]表示在i时刻砍了水果的最高分。

然后枚举前一次的砍的时刻k,因为在时刻k砍了的话,在k之前出现的水果都不见了。计算出从k+1到i这段时间才出现的,并且还没有消失的水果数量,那么就可以得到新的得分,dp[i] = max(dp[i], dp[k]+point(k+1,i))

point(k+1,i)表示k+1到i的得分。

那么问题就是,如何计算point(k+1,i)。

我们可以将水果出现的时刻和消失的时刻排序。

排序的时候,我是用下面的结构来排序的

struct Event{
	int id, f;
	Event(){}
	Event(int id, int f):id(id),f(f){}
	bool operator < (const Event &A)const{
		return id<A.id || (id==A.id && f<A.f);
	}
}e[N];
id表示对应的时刻,f为-1时表示在id这个时刻出现一个水果。

当f不为-1表示一个水果在f时刻出现,但在id时刻消失。


然后按时间顺序扫,遇到出现的,就在它出现的时刻+1,遇到消失的,就在它出现的时刻-1。

那么当我们枚举到时刻i时,此时还保留着的就是在i之前出现,并且还没有消失的水果。

然后我们求出k+1到i之间的水果数就OK了。

这里我们就可以用树状数组来维护了,典型的单点修改和区间更新的问题。


而对于这个问题,时刻的数字很大,需要离散化,我们可以通过枚举离散化之后的时刻就行了。


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 4010;
inline int lowbit(int x){
	return x&(-x);
}
int a[N], tot, m, b[N];
void add(int x, int v){
	tot+=v;
	for(; x<=m; x+=lowbit(x))	a[x]+=v;
}
int sum(int x){
	int res = 0;
	for(; x; x-=lowbit(x))	res += a[x];
	return res;
}
struct Event{
	int id, f;
	Event(){}
	Event(int id, int f):id(id),f(f){}
	bool operator < (const Event &A)const{
		return id<A.id || (id==A.id && f<A.f);
	}
}e[N];
int T, n, l, r, dp[N];
inline int point(int x){
	if(x>2)	return x;
	return 0;
}
int main(){
	scanf("%d", &T);
	for(int t=1; t<=T; t++){
		scanf("%d", &n);
		m = 0;
		for(int i=0; i<n; i++){
			scanf("%d %d", &l, &r);
			e[i<<1] = Event(l, -1);
			e[(i<<1)|1] = Event(r, l);
			b[m++] = l;
			b[m++] = r;
		}
		sort(e, e+n*2);
		sort(b, b+m);
		m = unique(b, b+m) - b;
		int ans = 0;
		memset(a, 0, sizeof(a));
		tot = 0;
		int j=0;
		n<<=1;
		for(int i=0; i<m; i++){
			while(j<n && e[j].id==b[i] && e[j].f==-1){
				add(i+1, 1);
				j++;
			}
			dp[i] = point(tot);
			for(int k=0; k<i; k++){
				dp[i] = max(dp[i], dp[k]+point(tot-sum(k+1)));
			}
			ans = max(ans, dp[i]);
			while(j<n && e[j].id==b[i] && e[j].f!=-1){
				int k = lower_bound(b, b+m, e[j].f) - b;
				add(k+1, -1);
				j++;
			}
		}
		printf("Case #%d: %d\n", t, ans);
	}
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值