动态规划_线性动态规划,区间动态规划

模板题链接:

数字三角形
最长递增子序列
最长公共子序列
石子合并

线性规划

数字三角形

思路:

状态表示: a r r [ i ] [ j ] arr[i][j] arr[i][j]表示从(1,1)出发到(i,j)的最长路径。
状态表示: a r r [ i ] [ j ] = m a x ( a r r [ i − 1 ] [ j − 1 ] , a r r [ i − 1 ] [ j ] ) + a r r [ i ] [ j ] arr[i][j]=max(arr[i-1][j-1],arr[i-1][j])+arr[i][j] arr[i][j]=max(arr[i1][j1],arr[i1][j])+arr[i][j]

代码:

#include<iostream>

using namespace std;

const int N = 505,INF=1e9;

int arr[N][N];

int main(){
    
    
    int n;
    cin>>n;
    for(int i=0;i<=n;i++){
    
    //初始化
        for(int j=0;j<=n;j++){
    
    
            arr[i][j]=-INF;
        }
    }
    
    for(int i=1;i<=n;i++){
    
    
        for(int j=1;j<=i;j++){
    
    
            cin>>arr[i][j];
        }
    }
    for(int i=2;i<=n;i++){
    
    //状态转移
        for(int j=1;j<=i;j++){
    
    
            arr[i][j]+=max(arr[i-1][j-1],arr[i-1][j]);
        }
    }
    int res=-INF;
    for(int i=1;i<=n;i++) res=max(res,arr[n][i]);
    cout<<res<<endl;
    return 0;
}

最长上升子序列:

思路:

状态表示: f [ i ] f[i] f[i]表示以i号数字结尾的最长公共自序列长度。
状态表示: f [ i ] = m a x { f [ j ] + 1 ∣ j ∈ { j ∣ a [ j ] < a [ i ] } } f[i]=max\{f[j]+1|j∈\{j|a[j]<a[i]\}\} f[i]=max{ f[j]+1j{ ja[j]<a[i]}}

代码:

#include<iostream>

using namespace std;

const int N = 1005;

int a[N],f[N];

int main(){
    
    
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
    
    
        f[i]=1;
        for(int j=1;j<i;j++){
    
    
            if(a[j]<a[i]){
    
    
               f[i]=max(f[i],f[j]+1); //状态转移
            }
        }
    }
    int res=0;
    for(int i=1;i<=n;i++){
    
    
        res=max(res,f[i]);
    }
    cout<<res<<endl;
    return 0;
}

最长公共子序列

思路:

状态表示: f [ i ] [ j ] f[i][j] f[i][j]表示a字符串的前i个字符和b字符串的前j个字符的最长公共自序列。
状态表示: f [ i ] [ j ] = m a x { f [ i − 1 ] [ j − 1 ] , f [ i − 1 ] [ j ] , f [ i ] [ j − 1 ] , f [ i − 1 ] [ j − 1 ] + 1 } f[i][j]=max\{f[i-1][j-1],f[i-1][j],f[i][j-1],f[i-1][j-1]+1\} f[i][j]=max{ f[i1][j1],f[i1][j],f[i][j1],f[i1][j1]+1}
分别表示不包括i和j位字符,选包括j位字符,包括i位字符,包括i位和j位字符。在这里插入图片描述

代码:

#include<iostream>

using namespace std;

const int N = 1e3+5;

int n,m,f[N][N];
char a[N],b[N];

int main(){
    
    
    int res=0;
    scanf("%d%d",&n,&m);
    scanf("%s%s",a+1,b+1);
    for(int i=1;i<=n;i++){
    
    
        for(int j=1;j<=m;j++){
    
    
            f[i][j]=max(f[i][j-1],f[i-1][j]);
            if(a[i]==b[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1); 
        }
    }
    cout<<f[n][m]<<endl;
    return 0;
}

区间动态规划

石子合并

思路:

状态表示: f [ l ] [ r ] f[l][r] f[l][r]表示合并从i到j区间的石子的最小代价。
状态表示: f [ l ] [ r ] = m i n { f [ l ] [ k ] + f [ k + 1 ] [ r ] + s u m ( a [ l ] , . . . , a [ r ] ) } , k ∈ [ l , r − 1 ] f[l][r]=min\{f[l][k]+f[k+1][r]+sum(a[l],...,a[r])\},k∈[l,r-1] f[l][r]=min{ f[l][k]+f[k+1][r]+sum(a[l],...,a[r])},k[l,r1]
一般区间长度从小到大循环。

代码:

#include<iostream>
#include<cstring>

using namespace std;

const int N = 305;

int a[N],f[N][N],s[N];

int main(){
    
    
    
    int n;
    cin>>n;
    
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    
    for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
    
    for(int len=2;len<=n;len++){
    
    
        for(int i=1;i+len-1<=n;i++){
    
    
            int l=i,r=i+len-1;
            f[l][r]=1e8;
            for(int j=l;j<r;j++){
    
    
                f[l][r]=min(f[l][r],f[l][j]+f[j+1][r]+s[r]-s[l-1]);
            }
        }
    }
    
    cout<<f[1][n]<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45931661/article/details/120012814