最长递增子序列(LIS)

一:

很容易想到的 DP的O(N^2)的复杂度

#pragma comment(linker,"/STACK:102400000,102400000")
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define clc(a, b) memset(a, b, sizeof(a))
const int inf = 0x3f;
const int INF = 0x3f3f3f3f;
const int maxn = 1000;
int n, a[maxn], dp[maxn];

int LIS()
{
    int i, j, k = 0;
    for(i = 0; i < n; i++)
    {
        dp[i] = 1;
        for(j = 0; j < i; j++)
        {
            if(a[i] > a[j] && dp[i] < dp[j] + 1)
            {
                dp[i] = dp[j] + 1;
            }
        }
        k = dp[i] > k ? dp[i] : k;
    }
    return k;
}
int main()
{
    while(~scanf("%d", &n))
    {
        clc(dp, 0);
        for(int i = 0; i < n; i++) 
            scanf("%d", &a[i]);
        printf("LIS = ");
        printf("%d
", LIS());
    }
}

二: 扩展升级版, 求定长的上升子序列个数

#pragma comment(linker,"/STACK:102400000,102400000")
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define clc(a, b) memset(a, b, sizeof(a))
const int inf = 0x3f;
const int INF = 0x3f3f3f3f;
const int maxn = 1000;
const int mod = 1000000007;
int n, m, a[maxn], dp[maxn][maxn], sum[maxn];
//sum 记录长度为i的子序列个数 dp[i][j] 记录从i开始长度为j的个数
int LIS()
{
    int i, j, k;
    for(i = 1; i <= n; i++)
    {
        dp[i][1] = 1;
        sum[1] = (dp[i][1] + sum[1]) % mod;
        for(k = 2; k <= i; k++)
        {
            for(j = 1; j < i; j++)
            {
                if(a[i] > a[j])
                {
                    dp[i][k] = (dp[i][k] + dp[j][k-1]) % mod;
                }
            }
            sum[k] = (sum[k] + dp[i][k]) % mod;
            //printf("i = %d, sum[%d] = %d
", i, k, sum[k]);
        }
    }
    return 0;
}
int main()
{
    int q;
    while(~scanf("%d %d", &n, &q))//n 数组长度, q个询问
    {
        clc(dp, 0);
        clc(sum, 0);
        for(int i = 1; i <= n; i++) 
            scanf("%d", &a[i]);
        LIS();
        while(q--)
        {
            scanf("%d", &m);
            printf("%d
", sum[m]);
        }
    }
    return 0;
}

三: DP + 二分法

链接: 原理+解释很详细http://www.felix021.com/blog/read.php?1587

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>

using namespace std;

const int inf = 0x3f;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5+5;

int lis[maxn], a[maxn], n;

int BinSearch(int len, int x)
{
    int left = 0, right = len - 1;
    while(left <= right)
    {
        int mid = (left + right) / 2;
        if(lis[mid] <= x)
        {
            left = mid + 1;
        }
        else 
        {
            right = mid - 1;
        }
    }
    return left;
}
int LIS()
{
    lis[0] = a[0];
    int len = 1;
    for(int i = 1; i < n; i++)
    {
        if(a[i] > lis[len-1])
        {
            lis[len++] = a[i];
        }
        else
        {
            int pos = BinSearch(len, a[i]);
            lis[pos] = a[i];
        }
    }
    return len;
}
int main()
{
    while(~scanf("%d", &n))
    {
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
        }

        int ans = LIS();
        printf("%d
", ans);
    }
}