1. 程式人生 > >【hdu】4521 小明序列【LIS變種】【間隔至少為d】

【hdu】4521 小明序列【LIS變種】【間隔至少為d】

疑問 size strong print AC 記錄 沒有 none main

題目鏈接: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; }
View Code

ACLIS解法

#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

【hdu】4521 小明序列【LIS變種】【間隔至少為d】