动态规划--最长递增子序列(LIS)-最长公共子序列(LCS)

LIS
最长递增子序列,朴素的是o(n^2)算法,二分下可以写成o(nlgn):维护一个当前最优的递增序列——找到恰好大于它更新
LCS

最长公共子序列,通常o(n^2)的算法

********************************************************************************************************************

LIS:
题目大意:给定序列个数n及n个数,求该序列的最大连续子序列的和,要求输出最大连续子序列的和以及子序列的首位位置

解题思路:经典DP,可以定义 dp[i]表示以a[i]为结尾的子序列的和的最大值,因而最大连续子序列及为dp数组中的最大值。
状态转移方程:dp[1] = a[1]; //以a[1]为结尾的子序列只有a[1];
                        i >= 2时, dp[i] = max( dp[i-1]+a[i],  a[i] );  
/* HDU 1003 Max Sum --- 经典DP */  
#include <cstdio>#include <cstring>  
int dp[100005];  
int main()   
{  
    int t, n;      
    int kase = 0;      
    int fst, lst, maxSum; //记录首位位置以及最大和      
    int start; //start是用于记录中间变化的起点的      
    scanf("%d", &t);      
    while (t--)  
    {  
        scanf("%d", &n);  
        for (int i = 0; i < n; ++i)  
        {  
            scanf("%d", dp + i);  
        }  
        start = fst = lst = 0;  
        maxSum = dp[0];  
        for (int i = 1; i < n; ++i)  
        {            //dp[i] = MAX(dp[i - 1] + dp[i], dp[i]);             
            if (dp[i - 1] >= 0)  
            {  
                dp[i] = dp[i - 1] + dp[i];  
            }  
            else  
            {  
                start = i; //抛弃dp[i-1],则起点发生变化            }  
  
                if (dp[i] > maxSum)  
                {                //若当前求得的子序列和最大,进行更新                 
                    maxSum = dp[i];  
                    fst = start;  
                    lst = i;  
                }  
            }  
            if (kase)  
            {  
                printf("\n");  
            }  
            printf("Case %d:\n", ++kase);  
            printf("%d %d %d\n", maxSum, fst + 1, lst + 1);  
        }  
    }  
    return 0;  
}
例:hdu 1087 Super Jumping!


题意:求最长递增子序列
          状态方程:dp[i]=max{dp[j]}+a[i];  其中,0<=j<=i, a[j]<a[i]      
#include<iostream>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
int a[1010], dp[1010];  
#define inf 0x3f3f3f3f  
  
int main()  
{  
    int n;  
    while (cin >> n&&n)  
    {  
        memset(a, 0, sizeof(a));  
        memset(dp, 0, sizeof(dp));  
        for (int i = 1; i <= n; i++) cin >> a[i];  
        int ans, maxn = 0;  
  
        for (int i = 1; i <= n; i++)  
        {  
            ans = -inf;  
            for (int j = 0; j < i; j++)  
            {  
                if (a[i] > a[j]) ans = max(ans, dp[j]);  
            }  
            dp[i] = ans + a[i];  
            maxn = max(maxn, dp[i]);  
        }  
        cout << maxn << endl;  
    }  
    return 0;  
}
*************************************************************************************************************

LCS:
  abcfbc   abfcab。


辅助空间变化示意图


可以看出:

F[i][j]=F[i-1][j-1]+1;(a[i]==b[j])

F[i][j]=max(F[i-1][j],F[i][j-1])(a[i]!=b[j]);

n由于F(i,j)只和F(i-1,j-1), F(i-1,j)和F(i,j-1)有关, 而在计算F(i,j)时, 只要选择一个合适的顺序, 就可以保证这三项都已经计算出来了, 这样就可以计算出F(i,j). 这样一直推到f(len(a),len(b))就得到所要求的解了.

#include<stdio.h>  
#include<string.h>  
int f[1001][1001];  
  
int main()  
{   
    char a[1001], b[1001];   
    int i, j, len1, len2;  
    while (~scanf("%s %s", a, b))   
    {   
        len1 = strlen(a);   
        len2 = strlen(b);  
        for (i = 0; i <= len1; i++)   
        {   
            f[i][0] = 0;   
        }   
        for (i = 0; i <= len2; i++)   
        {   
            f[0][i] = 0;   
        }  
        for (i = 1; i <= len1; i++)   
        {   
            for (j = 1; j <= len2; j++)   
            {   
                if (a[i - 1] == b[j - 1])  
                {   
                    f[i][j] = f[i - 1][j - 1] + 1;  
                }  
                else   
                {   
                    f[i][j] = f[i - 1][j]>f[i][j - 1] ? f[i - 1][j] : f[i][j - 1];   
                }  
            }   
        }  
        printf("%d\n", f[len1][len2]);   
    }   
    return 0;  
}

解题思路:这道题就是给你两个单词,然后你要把两个单词拼接成一个新单词,使得新单词的子序列中包含两个单词,并且要使这个新单词最短。所以这道题就是求最长公共子序列,并且要记录下子序列的字母,以及他们在主串和副串中的原始位置,之后进行拼接输出。

Sample Input
apple peach  ananas banana  pear peach
Sample Output
appleach  bananas  pearch
递归输出:
#include<iostream>  
#include<cstring>  
#include<string>  
#include<cstdio>  
#include<algorithm>  
using namespace std;  
#define inf 0x3f3f3f3f  
char s1[110], s2[110];  
int dp[110][110], mark[110][110];  
int len1, len2;  
  
void LCS()  
{  
    int i, j;  
    memset(dp, 0, sizeof(dp));  
    for (i = 0; i <= len1; i++)  mark[i][0] = 1;  
    for (i = 0; i <= len2; i++)  mark[0][i] = -1;  
  
    for (i = 1; i <= len1; i++)  
    {  
        for (j = 1; j <= len2; j++)  
        {  
            if (s1[i - 1] == s2[j - 1])  
            {  
                dp[i][j] = dp[i - 1][j - 1] + 1;  
                mark[i][j] = 0;  
            }  
            else if (dp[i - 1][j] >= dp[i][j - 1])  
            {  
                dp[i][j] = dp[i - 1][j];  
                mark[i][j] = 1;  
            }  
            else  
            {  
                dp[i][j] = dp[i][j - 1];  
                mark[i][j] = -1;  
            }  
        }  
    }  
  
}  
  
void PrintLCS(int i, int j)  
{  
    if (!i && !j) return;  
    if (mark[i][j] == 0)  
    {  
        PrintLCS(i - 1, j - 1);  
        //cout << s1[i - 1];  
        printf("%c", s1[i - 1]);  
  
    }  
    else if (mark[i][j] == 1)  
    {  
        PrintLCS(i - 1, j);  
    //  cout << s1[i - 1];  
        printf("%c", s1[i - 1]);  
  
    }  
    else  
    {  
        PrintLCS(i, j - 1);  
    //  cout << s2[j - 1];  
        printf("%c", s2[j - 1]);  
  
    }  
  
}  
  
int main()  
{  
    while (~scanf("%s%s", s1, s2))  
    {  
        len1 = strlen(s1);  
        len2 = strlen(s2);  
        LCS();  
        PrintLCS(len1,len2);  
        cout << endl;  
    }  
    return 0;  
}

推荐:

uva 10635 Prince and Princess LCS转化成LIS

hdu 4352 XHXJ's LIS 数位dp+LIS思想

srm div2 1000  状态压缩+LIS

poj 1239 Increasing Sequence 两次dp


uva 111 History Grading 要先排个序

poj 1080 Human Gene Functions

End

猜你喜欢

转载自blog.csdn.net/qq_34777600/article/details/79682184
今日推荐