动态规划 LCS & LIS

HDOJ 1159 Common Subsequence

动态规划:最长公共子序列( Longest Common Subsequence, LCS )问题。


AC代码。使用二维数组。
递推公式:

d p [ i ] [ j ] = { d p [ i − 1 ] [ j − 1 ] + 1 X i = Y i , i > 0 , j > 0 m a x { d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j ] } X i ≠ Y i , i > 0 , j > 0 dp[i][j] = \begin{cases} dp[i-1][j-1]+1{\qquad}X_i=Y_i,i>0,j>0 \\ max\{dp[i][j-1],dp[i-1][j]\}{\qquad}X_i { \ne } Y_i,i>0,j>0 \end{cases} dp[i][j]={ dp[i1][j1]+1Xi=Yi,i>0,j>0max{ dp[i][j1],dp[i1][j]}Xi=Yi,i>0,j>0
d p [ ] [ ] dp[][] dp[][]表示子序列 X i X_i Xi Y i Y_i Yi 的最长公共子序列的长度。

#include<iostream>
#include<cstring>
using namespace std;
int dp[1010][1010];
string str1,str2;
int lcs(){
    
    
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=str1.length();i++){
    
    
        for(int j=1;j<=str2.length();j++){
    
    
            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]);
        }
    }
    return dp[str1.length()][str2.length()];
}
int main(){
    
    
    while ( cin>>str1>>str2 ){
    
    
        cout<<lcs()<<endl;
    }
    return 0;
}

AC代码。使用滚动数组

#include<iostream>
#include<cstring>
using namespace std;
int dp[2][1010];
string str1,str2;
int lcs(){
    
    
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=str1.length();i++){
    
    
        for(int j=1;j<=str2.length();j++){
    
    
            if(str1[i-1]==str2[j-1]) dp[i%2][j] = dp[(i-1)%2][j-1]+1;
            else dp[i%2][j] = max(dp[(i-1)%2][j],dp[i%2][j-1]);
        }
    }
    return dp[str1.length()%2][str2.length()];
}
int main(){
    
    
    while ( cin>>str1>>str2 ){
    
    
        cout<<lcs()<<endl;
    }
    return 0;
}

使用二维数组并且增加打印功能。
增加一个数组 path[1010][1010][2] 记录每一步中 dp[ i ][ j ] 的来源情况。
从结果逐步倒推,得到公共子序列的逆序。

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
int dp[1010][1010];
int path[1010][1010][2];
string str1,str2;
int lcs(){
    
    
    memset(dp,0,sizeof(dp));
    memset(path,0,sizeof(path));
    for(int i=1;i<=str1.length();i++){
    
    
        for(int j=1;j<=str2.length();j++){
    
    
            if(str1[i-1]==str2[j-1]){
    
    
                dp[i][j] = dp[i-1][j-1]+1;
                path[i][j][0]=-1;
                path[i][j][1]=-1;
            }
            else if(dp[i-1][j]>dp[i][j-1]){
    
    
                dp[i][j] = dp[i-1][j];
                path[i][j][0] = -1;
                path[i][j][1] = 0;
            }else{
    
    
                dp[i][j] = dp[i][j-1];
                path[i][j][0] = 0;
                path[i][j][1] = -1;
            }
        }
    }
    return dp[str1.length()][str2.length()];
}
int printpath(){
    
        //打印子序列
    stack<char>v;
    int i=str1.length(),j=str2.length();
    while(i&&j){
    
    
        if(str1[i-1]==str2[j-1]) v.push(str1[i-1]);
        int a=path[i][j][0];
        j += path[i][j][1];
        i += a;
    }
    int len=v.size();
    for(i=0;i<len-1;i++){
    
    
        printf("%c ",v.top());
        v.pop();
    }
    printf("%c\n",v.top());
    v.pop();
    return 0;
}
int main(){
    
    
    while ( cin>>str1>>str2 ){
    
    
        cout<<lcs()<<endl;
        printpath();
    }
    return 0;
}

HDOJ 1257 最少拦截系统

先用 贪心法 解决问题。
对于每次来的导弹,优先用现有拦截系统中可以拦截它且拦截高度最低的系统,去拦截;
如果没有可以拦截的系统,就增加一个系统。

#include<bits/stdc++.h>
using namespace std;
int main(){
    
    
    int n;
    while (~scanf("%d",&n)){
    
    
        int fire[n];
        vector<int>num; //所有拦截系统的高度,且顺序是递增
        for(int i=0;i<n;i++){
    
    
            scanf("%d",&fire[i]);
            if(num.empty()) num.push_back(fire[i]); //初始拦截系统为空
            else for(int j=0,len=num.size();j<len;j++)
                    if(num[j]>fire[i]){
    
     //找到可以拦截且高度最低的系统
                        num[j]=fire[i];
                        break;
                    }else if(j==num.size()-1) num.push_back(fire[i]); //找不到,增加新系统
        }
        printf("%d\n",num.size());
    }
    return 0;
}

使用 最长递增子序列( LIS ) 解决问题。
求出高度序列的 LIS。LIS 的长度就是拦截系统的个数。


方法一:借助 LCS。
首先对导弹高度序列 A 递增排序得到序列 A’
然后对 A 和 A’ 求最长公共子序列LCS
结果就等于 A 的 LIS 的长度
复杂度等于求LCS的复杂度,是O( n 2 n^2 n2)


方法二:使用DP算法。
首先定义状态 dp[ i ],表示以第 i 个数 A i A_i Ai 为结尾的最长递增子序列的长度;
dp[ i ] = max{ 0, dp[ j ] } + 1 ( 0<j<i 且 A j < A i A_j<A_i Aj<Ai )
最后,max{ dp[ i ] | 0<=i<n }就是 LIS 长度,就是拦截系统的个数。
复杂度也是O( n 2 n^2 n2)


方法三:不使用DP,利用序列本身的特征,借助一个数组找出 LIS 的长度。
首先,定义数组 d[],它的长度为 len 。
初始化 d[ 1 ] 为 high[ 1 ],len = 1
开始遍历 high:( i>=2 )
如果 high[ i ] 大于 d[ len ], d[ ++len ] = high[ i ]
如果 high[ i ] 小于等于 d[ len ],就找出 d[] 中第一个大于 high[ i ] 的数字并用 high[ i ] 替换掉它
最后 len 就是 LIS 的长度,就是拦截系统的个数。
复杂度为O( n l o g 2 n nlog_2n nlog2n)

方法一:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 10000;
int dp[2][MAXN];
int high[MAXN],highs[MAXN];
int n;
int lcs(){
    
    
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++){
    
    
        for(int j=1;j<=n;j++){
    
    
            if(high[i - 1] == highs[j - 1]) dp[i % 2][j] = dp[(i - 1) % 2][j - 1] + 1;
            else dp[i%2][j] = max(dp[(i-1)%2][j],dp[i%2][j-1]);
        }
    }
    return dp[n%2][n];
}
int main(){
    
    
    while (~scanf("%d",&n)){
    
    
        for(int i=0;i<n;i++){
    
    
            scanf("%d",&high[i]);
            highs[i] = high[i];
        }
        sort(highs, highs + n);
        printf("%d\n",lcs());
    }
    return 0;
}

方法二

扫描二维码关注公众号,回复: 13133583 查看本文章
#include<iostream>
using namespace std;
const int MAXN = 10000;
int high[MAXN],n;
int lis(){
    
    
    int dp[MAXN];
    dp[1] = 1;
    int maxidp = 1;         //记录dp中最大的那一个
    for(int i=2;i<=n;i++){
    
      //递推计算dp[i]
        int maxjdp = 0;
        for(int j=1;j<i;j++)
            if(high[j]<high[i] && dp[j]>maxjdp) maxjdp = dp[j];
        dp[i] = maxjdp+1;
        maxidp = maxidp<dp[i]?dp[i]:maxidp;
    }
    return maxidp;
}
int main(){
    
    
    while (~scanf("%d",&n)) {
    
    
        for(int i=1;i<=n;i++) scanf("%d",&high[i]);
        printf("%d\n",lis());
    }
    return 0;
}

方法三

#include<iostream>
using namespace std;
const int MAXN = 10000;
int high[MAXN],n;
int lis(){
    
    
    int d[n],len = 1;;
    d[1] = high[1];
    for(int i=2;i<=n;i++){
    
    
        if(high[i]>d[len]) d[++len] = high[i];
        else{
    
    
            int t = lower_bound(d+1,d+len+1,high[i]) - d;
            d[t] = high[i];
        }
    }
    return len;
}
int main(){
    
    
    while (~scanf("%d",&n)) {
    
    
        for(int i=1;i<=n;i++) scanf("%d",&high[i]);
        printf("%d\n",lis());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46523755/article/details/115276919