hdu4521 小明序列LIS变种间隔至少为d
Posted is_ok
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu4521 小明序列LIS变种间隔至少为d相关的知识,希望对你有一定的参考价值。
题目链接:https://vjudge.net/contest/228455#problem/B
转载于:https://blog.csdn.net/a709743744/article/details/51765252
题目大意:
求最长上升子序列,其中子序列中相邻的两个数的下标差要超过k
解题分析:
子序列中相邻的两个数的下标要超过k,要想满足这个条件我们可以按下面的思路想:
首先nlogn的LIS是毫无疑问的,然后再这个算法中,我们每次二分找到当前数的位置,如果数组中的数比当前数大的话就更新数组
所以我们可以稍微改一下上述步骤,当我们二分计算当前数的位置时,只是把当前数应该在数组中的位置保存下来,当前只更新在i - k之前的那个数,
这样我们就可以保证每次二分查找时,数组中的所有数的下标都比当前的下标少至少k.
然而我还是没有弄懂,先记录着吧。
这是我的代码,用结构体,然后套用了一下LIS模板,不知道为什WA了
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; const int MAXN = 1e5 + 100; int n, d; struct node { int val, ord; }arr[MAXN],lis[MAXN]; int find(int l, int r, int key) { if (l == r)return l; int mid = (l + r) >> 1; if (key>lis[mid].val)return find(mid + 1, r, key); else return find(1, mid, key); } int main() { while (scanf("%d %d", &n, &d) != EOF){ //注意是下标之差大于d,而不是值之差大于d memset(arr, 0, sizeof(arr)); for (int i = 1; i <= n; i++) { scanf("%d", &arr[i].val); arr[i].ord = i; } int len = 0; for (int i = 1; i <=n; i++){ if (i == 1)lis[++len] = arr[i]; else if (arr[i].val > lis[len].val) { if ((arr[i].ord - lis[len].ord) > d)lis[++len] = arr[i]; } else { int j = find(1, len, arr[i].val); if (j != len){ if (j == 1){ if ((lis[2].ord - arr[i].ord) > d)lis[j] = arr[i]; } else{ if ((lis[j + 1].ord - arr[i].ord) > d && (arr[i].ord - lis[j - 1].ord) > d) lis[j] = arr[i]; } } } } printf("%d\n", len); } return 0; }
AC的LIS解法
#include<iostream> #include<cstring> #include<cstdio> #include <algorithm> #define maxn 100005 using namespace std; int a[maxn], b[maxn], p[maxn]; int n, d; int find(int p) //二分查找<=p的位置+1 { int l, r, mid; l = 1, r = n, mid = (l + r) >> 1; while (l <= r){ if (p>b[mid]) l = mid + 1; else if (p<b[mid]) r = mid - 1; else return mid; mid = (l + r) >> 1; } return l; } int LIS(){ int i, j, ans = 0; for (i = 1; i <= n; i++){ p[i] = find(a[i]); //p[i]存的是a[i]在上升数组中的位置 ans = max(ans, p[i]); j = i - d; if (j>0) b[p[j]] = min(b[p[j]], a[j]); } return ans; } int main() { int i, res; while (cin >> n >> d){ for (i = 1; i <= n; i++){ scanf("%d", &a[i]); b[i] = maxn; } res = LIS(); printf("%d\n", res); } return 0; }
dp AC解法
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define INF 0x3f3f3f3f using namespace std; const int maxn = 100000 + 10; int a[maxn], dp[maxn], g[maxn], n, k; int main() { while (~scanf("%d%d", &n, &k)) { int ans = -1; memset(dp, 0, sizeof(dp)); memset(g, INF, sizeof(g)); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) { //延迟p位更新 //为什么我感觉i>k+1以后还是连续的,下标并没有相差k啊???搞不懂 if (i - k - 1>0) g[dp[i - k - 1]] = min(a[i - k - 1], g[dp[i - k - 1]]); // i-p>1 是因为下标j范围为1<j<=m dp[i] = lower_bound(g + 1, g + 1 + n, a[i]) - g; //先记录下a[i]在g数组中的位置 ans = max(ans, dp[i]); } cout << ans << endl; } return 0; }
2018-05-17
以上是关于hdu4521 小明序列LIS变种间隔至少为d的主要内容,如果未能解决你的问题,请参考以下文章
hdu 4521 小明系列问题——小明序列(线段树+DP或扩展成经典的LIS)
HDU 1087 Super Jumping! Jumping! Jumping! (DP+LIS)