题意:赛车从x轴出发往前走,竖直方向速度为v,水平速度要在-v/r到v/r之间,给出n个钻石的坐标,问赛车最多能拿到多少颗钻石。
在打组队赛的时候,我就想到了动态规划加线段树优化或者是最长上升子序列的nlog(n)算法,但是都没有想到具体该怎么处理,之后证实这两种方法都能实现,不过由于线段树还是比较麻烦,所以只实现了最长上升子序列的方法。
这是一个很巧妙的处理,对于每一个钻石的坐标,都能根据水平速度与竖直速度的关系映射到x轴上的一个区间上,对应的y越大,区间长度就越长。对于两个区间,如果区间a的起点大于等于区间b的起点,而且区间a的终点小于等于b的终点,那么就可以先拿a点的钻石,之后去拿b点的钻石,如果a的起点小于b的起点的话,无论终点怎样都不可能先拿a再拿b,于是问题就变成了对于n个区间,按照区间的起点降序排序,然后找终点的最长不下降子序列。数据量比较大,但是最长上升子序列有nlog(n)的算法,我抄的模板,也并不是很懂。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 100005;
typedef long long ll;
struct node {
ll b, e;
}id[MAXN];
bool cmp(node a, node b) {
if(a.b != b.b)
return a.b > b.b;
return a.e < b.e;
}
ll dp[MAXN];
int Search(ll num, int low, int high) {
int mid;
while(low <= high) {
mid = (low + high) >> 1;
if(num >= dp[mid]) low = mid + 1;
else high = mid - 1;
}
return low;
}
int DDPP(int n) {
int i, len = 1, pos;
dp[1] = id[1].e;
for(i = 2; i <= n; i++) {
if(id[i].e >= dp[len]) {
len = len + 1;
dp[len] = id[i].e;
}
else {
pos = Search(id[i].e, 1, len);
dp[pos] = id[i].e;
}
}
return len;
}
int main() {
int i, n, r, w, h, x, y;
while(~scanf("%d%d%d%d", &n, &r, &w, &h)) {
for(i = 1; i <= n; i++) {
scanf("%d%d", &x, &y);
id[i].b = (ll)x * r - y;
id[i].e = (ll)x * r + y;
}
sort(id + 1, id + n + 1, cmp);
printf("%d\n", DDPP(n));
}
return 0;
}