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;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值