题意:水果忍者。。。给定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;
}