动态规划--最长上升子序列LIS

来源:互联网 发布:淘宝店铺卖家等级 编辑:程序博客网 时间:2024/06/05 13:30

1.记录以Ak结尾的最长增长子序列的长度 O(n^2)

#include <iostream>

#include <cstdio>

#include <algorithm>

using namespace std;

const int maxn =1005;

int num[maxn];

int maxlen[maxn];


int main()

{

    int n;

    cin >> n;

    for (int i =1; i <= n; i ++) {

        scanf("%d",&num[i]);

    }

    fill(maxlen + 1, maxlen + n +1,1);

    for (int i =2; i <= n; i ++) {

        for (int j = i -1; j >0; j --) {

            if (num[i] > num[j]) {

                maxlen[i] = max(maxlen[i],maxlen[j] + 1);//maxlen[k]记录以Ak结尾的最长增长子序列的长度

            }//于是,每次寻找,在Ak左边,并且,数字比Ak小的数Aj,用maxlen[j] + 1不断更新

        }

    }

    cout << *max_element(maxlen + 1, maxlen + n +1) << endl;

    return0;

}


2.O(n^2)

根据图得

每个端点可以选择向后面比他大的点走

而往哪个点走,取决于往哪走可以得到最大值lis

对于最后一个点来说,在他前面比他小的点都可以到达他

而从lis,的起点到终点,中间经过更多点,则增长子序列更长

位置idp[i]表示lis长度

得到1.递推 dp[i] = max(dp[i],dp[j] +1)对每个点i,在他前面的点j,如果小于他,那么最大值,可能是,lis没有j->dp[i],或者,经过j->dp[j] + 1

    2.边界初始值全为1,一个数就是他的增长子序列。


代码同上,i = 2 to n,保证i之前的j的maxlen都是有效的(因为i要用到他所有前面的j的maxlen)


3.二分查找 O(nlgn)

用b[k]存上升子序列长度为k时,子序列结尾的最小值。

若i < j,b[i] < b[j]

若num[i] > b[k],b[k + 1] = num[i],k ++

若num[i] < b[k],在b[1]..b[k - 1]中寻找大于num[i]的最小值,即第一个大于num[i]的位置pos,b[pos] = min(b[pos],num[i]),maxlen[i] = pos

二分查找,所以是logn


#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespacestd;

const int maxn =500005;

int num[maxn];

int b[maxn];

int maxlen[maxn];


int solve(int n)

{

    int k =1;

    b[1] =num[1];

    maxlen[1] =1;

    for (int i =2; i <= n; i ++) {

        if (num[i] >=b[k]) {

            maxlen[i] = k +1;

            b[k +1] =num[i];

            k ++;

        }

        else{

            int pos =lower_bound(b +1,b + k,num[i]) -b;//b[1]..b[k - 1]里找

            maxlen[i] = pos;

            b[pos] =min(b[pos],num[i]);

        }

    }

    return k;

}

void output(int res)

{

    if (res ==1) {

        printf("My king, at most 1 road can be built.\n");

    }

    else printf("My king, at most %d roads can be built.\n",res);


}

int main()

{

    int n,p,r;

    int ce =0;

    while (scanf("%d",&n) !=EOF) {

        memset(num,0,sizeof(num));

        memset(maxlen,0,sizeof(maxlen));

        memset(b,0,sizeof(b));

        for (int i =0; i < n; i ++) {

            scanf("%d%d",&p,&r);

            num[p] = r;

         

        }

        printf("Case %d:\n",++ce);

        output(solve(n));

        printf("\n");

    }

    return0;

}


记录路径 O(n^2)

#include <iostream>

#include <algorithm>

#include <cstdio>

#include <vector>

using namespacestd;

const int maxn =1005;

struct mouse{

    int w,s,id;

   booloperator < (constmouse &a) const

    {

        if(w < a.w)returntrue;

        elseif(w == a.w &&s > a.s)returntrue;

        elsereturnfalse;

    }

}mouse[maxn];//weigh speed


int maxlen[maxn];

int pre[maxn];

void lds(int cnt)

{

    for (int i =1; i <= cnt; i ++) {

        pre[i] =0;

        maxlen[i] =1;

    }

    

    for (int i =2; i <= cnt; i ++) {

        for (int j =1; j < i; j ++) {

            if (mouse[i].w >mouse[j].w &&mouse[i].s <mouse[j].s &&maxlen[i] < maxlen[j] +1) {//要求w严格上升,ij之后不一定就大

                pre[i] = j;

                maxlen[i] =1 +maxlen[j];

            }

        }

    }


    int len =1,num =1;

    for (int i =1; i <= cnt; i ++) {

        if (maxlen[i] > len) {

            num = i;

            len = maxlen[i];

        }

    }

    vector<int> v;

    v.push_back(num);

    for (int i = len -1; i >0; i --) {

        num = pre[num];

        v.push_back(num);

    }

    cout << len <<endl ;

    for (int i = len -1; i >=0; i --) {

        cout <<mouse[v[i]].id <<endl;

    }

    

}

int main()

{

    int cnt =1;

    while (cin >>mouse[cnt].w >>mouse[cnt].s) {

         mouse[cnt].id = cnt;

        cnt ++;

    }

    cnt --;

    

    sort(mouse +1,mouse +1 + cnt);


      //mouse_s里的严格最大下降子序列

    lds(cnt);

    return0;

}