题意:题目的意思很简单,给定一个序列,求最大递增子序列的长度,同时相邻俩项下标的差值必须大于d
分析:就题意而已,只是在最长递增子序列的加了一个限制条件,沿用最长递增子序列的思路,则dp[i] = max(dp[i], dp[j] + 1), (i - j > d && a[i] > a[j])
从状态转移方程可以看出,问题的关键在于维护区间(0,i - d - 1) 最大的dp值,且对应值小于a[i]
我们换一种角度,用a[i]的值作为下标,保证当前区间(0, a[i] - 1) 中的dp值都满足 (i - j > d) , 那么dp[i] 就等于区间(0, a[i] - 1) 的最大值, 这个就可以用线段树来解决了
hdu4521
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> #include<stdlib.h> using namespace std; const int N = 100000 + 10; struct node { int l, r, max; }p[N * 3]; int a[N], d, n; int dp[N]; void build(int s, int t, int k) { p[k].l = s; p[k].r = t; p[k].max = 0; if( s == t ) return ; int mid = (s + t) >> 1, kl = k << 1, kr = kl + 1; build(s, mid, kl); build(mid + 1, t, kr); } void insert(int k, int s, int val) { if( p[k].l == p[k].r && p[k].l == s) { p[k].max = max(p[k].max, val); return ; } int mid = (p[k].l + p[k].r) >> 1, kl = k << 1, kr = kl + 1; if( s <= mid) insert(kl, s, val); else insert(kr, s, val); p[k].max = max(p[kl].max, p[kr].max); } int query(int k, int s, int t) { if(s <= p[k].l && t >= p[k].r) { return p[k].max; } int mid = (p[k].l + p[k].r) >> 1, kl = k << 1, kr = kl + 1; int a = 0, b = 0; if(s <= mid) a = query(kl, s, t); if(t > mid) b = query(kr, s, t); return max(a, b); } int main() { while(scanf("%d %d", &n, &d) == 2) { int maxa = 0; for(int i = 1; i <= n; ++i) { scanf("%d", a + i); ++a[i]; maxa = max(maxa, a[i]); } memset(dp, 0, sizeof(dp)); build(0, maxa, 1); int ans = 1; for(int i = 1; i <= n; ++i) { int tmp = query(1, 0, a[i] - 1); dp[i] = max(dp[i], tmp + 1); ans = max(ans, dp[i]); if(i - d > 0) insert(1, a[i - d], dp[i - d]);//在线段树中插入满足下标满足的DP值 } printf("%d\n",ans); } return 0; }