zoukankan      html  css  js  c++  java
  • DP基础(LIS和LCS)

    目录

     

    LIS(最大上升子序列)

    思路

    模板

    LCS(最长公共子序列)

    思路

    模板

    例题

    A:POJ-1836 Alignment

    B:POJ-2533 Longest Ordered Subsequence

    C:HDU-1159 Common Subsequence

    D:POJ-2250 Compromise

    E:POJ-2264 Advanced Fruits

    F:HDU-1160 FatMouse's Speed


    LIS(最大上升子序列)

    思路

    状态设计:dp[i]代表以a[i]结尾的LIS的长度
    状态转移:dp[i]=max(dp[i], dp[j]+1) (1<=j< i, a[j]< a[i])
    边界处理:dp[i]=1 (1<=i<=n)
    时间复杂度:O(N^2)

    模板

    #include<iostream>
    #include<string>
    #include<cstring>
    #include<algorithm>
    #define MAXN 1000
    using namespace std;
    typedef long long ll;
    int ans[MAXN+5];
    int dp[MAXN+5];
    int main()
    {
    	int n,re=1;
    	cin>>n;
    	for(int i=1;i<=n;++i){
    		cin>>ans[i];
    	}
    	for(int i=1;i<=n;++i){
    		dp[i]=1;
    		for(int j=1;j<=i-1;++j){
    			if(ans[j]<ans[i]) dp[i]=max(dp[i],dp[j]+1);
    		}
    		re=max(re,dp[i]);
    	}
    	cout<<re<<endl;
     } 
    

    LCS(最长公共子序列)

    思路(转自LCS

    dp[i][j]表示字符串x的前i个字符构成的子串和字符串y的前j个字符构成的子串的最长公共子序列的长度

    状态转移方程

    i=0 or j=0说明有一个字符串的长度为0,因此最长公共组序列长度为0。当字符串x的第i个字符和字符串y的第j个字符相等时,dp[i][j]就等于dp[i-1][j-1]+1,如果不等dp[i][j]=max(dp[i][j-1],dp[i-1][j])

    模板

    int dp[1000][1000]
    int Lcs(string x,string y)
    {
    	for(int i=0;i<=x.length();i++)
    	  for(int j=0;j<=y.length();j++)
    	    if(i==0||j==0)
    	      dp[i][j]=0;
    	    else if(x[i-1]==y[j-1])
    	      dp[i][j]=dp[i-1][j-1]+1;
    	    else
    	      dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    	return dp[x.length()][y.length()];
    }

    例题

    A:POJ-1836 Alignment:LIS变形题,看懂题意应该就能写出来,看不懂的可以看Alignment题解,AC代码:

    #include <cstring>
    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    using namespace std;
    const int maxn=1e4+7;
    const int INF=0x3f3f3f3f;
    double hige[maxn];
    double size1[maxn],dp[maxn],dp1[maxn];
    int main()
    {
        int n;
        while(cin>>n){
            for(int i=1;i<=n;i++){
                cin>>hige[i];
            }
            for(int i=1;i<=n;i++){
    			dp[i]=1;
    		}
            for(int i=1;i<=n;i++){
               for(int j=1;j<i;j++){
                   if(hige[j]<hige[i])
                      dp[i]=max(dp[i],dp[j]+1);
               }
            }
            for(int i=1;i<=n;i++){
    			dp1[i]=1;
    		}
    		for(int i=n;i>=1;i--){
               for(int j=i+1;j<=n;j++){
                   if(hige[j]<hige[i])
                      dp1[i]=max(dp1[i],dp1[j]+1);
               }
            }
            int ans=0;
            for(int i=1;i<=n;i++)
                for(int j=i+1;j<=n;j++)
                   if(dp[i]+dp1[j]>ans)
                       ans=dp[i]+dp1[j];
            cout<<n-ans<<endl;
        }
        return 0;
    }

    B:POJ-2533 Longest Ordered Subsequence:本题考查LIS的O(nlogn)算法,具体解析可看(最长上升子序列(LIS)长度的O(nlogn)算法),AC代码:

    #include <cstring>
    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    using namespace std;
    const int maxn=1e3+7;
    const int INF=0x3f3f3f3f;
    int a[maxn],s[maxn],tmp;
    int main()
    {
        int n;
        while(cin>>n){
            for(int i=1;i<=n;i++)
                cin>>a[i];
            int tot=1;
            s[tot]=a[1];
            for(int i=2;i<=n;i++){
                if(a[i]>s[tot])
                    s[++tot]=a[i];
                else{
                    tmp=lower_bound(s+1,s+tot+1,a[i])-s;
                    s[tmp]=a[i];
                }
            }
            cout<<tot<<endl;
        }
        return 0;
    }

    C:HDU-1159 Common Subsequence:LCS最长公共子序列裸体,AC代码:

    #include <cstring>
    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    using namespace std;
    const int maxn=1e3+7;
    const int INF=0x3f3f3f3f;
    char str1[maxn],str2[maxn];
    int dp[maxn][maxn];
    int main()
    {
        while(cin>>str1>>str2){
            int n=strlen(str1),m=strlen(str2);
            memset(dp,0,sizeof(dp));
            for(int i=0;i<=n;i++)
            for(int j=0;j<=m;j++){
                if(i==0||j==0)
                    dp[i][j]=0;
                else if(str1[i-1]==str2[j-1])
                    dp[i][j]=dp[i-1][j-1]+1;
                else
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
            cout<<dp[n][m]<<endl;
        }
        return 0;
    }

    D:POJ-2250 Compromise:LCS的记录路径问题的模板题。看下代码应该就能明白:

    #include <cstring>
    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    using namespace std;
    const int maxn=1e2+7;
    const int INF=0x3f3f3f3f;
    char str1[maxn][50],str2[maxn][50];
    int dp[maxn][maxn];
    void dfs(int i, int j)
    {
        if (i == 0 || j == 0)
            return;
        if (strcmp(str1[i-1],str2[j-1])==0){
            dfs(i - 1, j - 1);
            cout <<str1[i-1]<< " ";
        }
        else
        {
            if (dp[i - 1][j] > dp[i][j - 1])
                dfs(i - 1, j);
            else
                dfs(i, j - 1);
        }
    }
    int main()
    {
        char s[50];
        while(cin>>s){
            int n=0,m=0;
            while(strcmp(s,"#")!=0){
                 strcpy(str1[n++],s);
                 cin>>s;
            }
            cin>>s;
            while(strcmp(s,"#")!=0){
                 strcpy(str2[m++],s);
                 cin>>s;
            }
            memset(dp,0,sizeof(dp));
            for(int i=0;i<=n;i++)
            for(int j=0;j<=m;j++){
                if(i==0||j==0)
                    dp[i][j]=0;
                else if(strcmp(str1[i-1],str2[j-1])==0){
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
            dfs(n,m);
            cout<<endl;
        }
        return 0;
    }

    E:POJ-2264 Advanced Fruits:LCS记录路径 + 最短公共父序列。和上一题的区别是要把各自独特含有的输出出来,还是直接上代码,代码挺好理解的:

    #include <cstring>
    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    using namespace std;
    const int maxn=1e2+7;
    const int INF=0x3f3f3f3f;
    char str1[maxn],str2[maxn];
    int dp[maxn][maxn],ans[maxn][maxn];
    void dfs(int i, int j)
    {
        if(i==0&&j==0)//注意是且,因为要把各自独有的都输出完
            return ;
        if(ans[i][j]==0){
            dfs(i-1,j-1);
            cout <<str1[i-1];
        }
        else if (ans[i][j]==1){
            dfs(i-1, j);
            cout<<str1[i-1];
        }
        else{
            dfs(i,j-1);
            cout<<str2[j-1];
        }
    }
    int main()
    {
        while(cin>>str1>>str2){
            int n=strlen(str1),m=strlen(str2);
            memset(dp,0,sizeof(dp));
            for(int i=1;i<=n;i++)  //本题出现ans数组而上题不用的原因就在这和下面的四行的初始化,因为要输出各自独有的内容。
                ans[i][0]=1;
            for(int i=1;i<=m;i++)
                ans[0][i]=-1;
            for(int i=0;i<=n;i++)
            for(int j=0;j<=m;j++){
                if(i==0||j==0)
                    dp[i][j]=0;
                else if(str1[i-1]==str2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                    ans[i][j]=0;
                }
                else if(dp[i-1][j] >= dp[i][j-1]){
                    dp[i][j] = dp[i-1][j];
                    ans[i][j]=1;
                }
                else{
                    dp[i][j] = dp[i][j-1];
                    ans[i][j]=-1;
                }
            }
            dfs(n,m);
            cout<<endl;
        }
        return 0;
    }

    F:HDU-1160 FatMouse's Speed:LIS+结构体,因为排序的条件是两种,可构建结构体,对其中一个条件进行排序,因为要排序,而结果要输出原本的位置,所以结构体还要储存位置,剩下的就和LIS一样了,不过需要输出路径,所以这里的c[i] = j;意思就是当前这个最优解i是由上一个最优解j递推出来的。所以可以用这种方法求的最优路径。AC代码:

    #include <cstring>
    #include <iostream>
    #include <cstdio>
    #include <fstream>
    #include <algorithm>
    using namespace std;
    const int maxn1=1e3+7;
    const int INF=0x3f3f3f3f;
    int dp[maxn1],c[maxn1];
    struct mouse{
        int s,e;
        int index;
    }p[maxn1];
    bool cmp(mouse x,mouse y){
        return x.s<y.s;
    }
    void pri(int k){
        if(k==0)
            return ;
        pri(c[k]);
        cout<<p[k].index<<endl;
    }
    int main()
    {
        memset(c,0,sizeof(c));
        int n=1;
        while(scanf("%d%d",&p[n].s,&p[n].e)!=EOF){
           p[n].index=n;
           ++n;
        }
        sort(p+1,p+n+1,cmp);
        for(int i=0;i<=n;i++)
            dp[i]=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<i;j++)
                if(p[j].e>p[i].e&&p[i].s!=p[j].s&&(dp[j]+1>dp[i])){
                   dp[i]=dp[j]+1;
                   c[i]=j;
            }
        int maxn=0,maxm=0;
        for (int i=1;i<=n;i++)
    		if (maxn<dp[i]){
    			maxn=dp[i];
    			maxm=i;
    		}
    	cout<<maxn<<endl;
    	pri(maxm);
        return 0;
    }
  • 相关阅读:
    什么是restful风格?
    android中设置控件获得焦点
    Android中Parcelable的原理和使用方法
    webstorm快捷键大全
    HTTP状态码
    三行代码写爬虫
    HTTrackPortable
    安装mac系统
    Vue常用的操作指令
    什么样的人不适合当程序员呢?
  • 原文地址:https://www.cnblogs.com/shmilky/p/14089051.html
Copyright © 2011-2022 走看看