題意:賽車從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;
}