zoukankan      html  css  js  c++  java
  • ACwing(基础)--- 线性DP、区间DP

    数字三角形

    给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

    分析:

    • 状态表示f[i,j]

    集合:所有从起点走到(i,j)的路径
    属性:Max

    • 状态计算

    来自左上:f[i-1,j-1]+a[i,j]
    来自右上:f[i-1,j]+a[i,j]

    代码1:正击

    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    const int N = 510;
    const int INF = 0x3f3f3f3f;
    int n;
    int a[N][N],f[N][N];
    
    int main(){
        cin>>n;
        for(int i = 1;i<=n;i++)
            for(int j=1;j<=i;j++)
                cin>>a[i][j];
        for(int i = 0;i<=n;i++)
            for(int j=0;j<=i+1;j++)
                f[i][j]=-INF;
        f[1][1]=a[1][1];
        for(int i = 2;i<=n;i++)
            for(int j=1;j<=i;j++)
                f[i][j]=max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j]);
        int res=-INF;
        for(int i=1;i<=n;i++) res=max(f[n][i],res);
        cout<<res;
    }
    

    逆向思维:反手

    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    const int N = 510;
    
    int n;
    int f[N][N];
    
    int main(){
        cin>>n;
        for(int i = 1;i<=n;i++)
            for(int j=1;j<=i;j++)
                cin>>f[i][j];
        for(int i = n;i>=1;i--)
            for(int j=i;j>=1;j--)
                f[i][j]=max(f[i+1][j],f[i+1][j+1])+f[i][j];
        cout<<f[1][1];
    }
    
    

    最长上升子序列

    给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少。
    输入格式
    第一行包含整数N。
    第二行包含N个整数,表示完整序列。
    输出格式
    输出一个整数,表示最大长度。

    分析:

    • 状态表示f[i]

    集合:所有以第i个数结尾的上升子序列
    属性:Max

    • 状态计算

    f[i] = max(f[j]+1),j=0,1,2,..,i-1

    #include<iostream>
    
    using namespace std;
    const int N = 1e3+10;
    
    int n;
    int a[N],f[N];
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        
        for(int i=1;i<=n;i++)
        {
            f[i]=1; //a[i]自己 
            for(int j=1;j<i;j++)
            {
                if(a[j]<a[i])
                {
                    f[i]=max(f[i],f[j]+1); //前面有小于它的数,那么+1(状态转移)
                }
            }
        }
        int res=0;
        for(int i=1;i<=n;i++) res=max(res,f[i]);
        cout<<res;
        return 0;
    }    
        
    

    还原最长上升子序列

    #include<iostream>
    
    using namespace std;
    
    const int N = 1e3+10;
    
    int n;
    int a[N],f[N],g[N];
    
    int main(){
        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]){
                    if(f[i]<f[j]+1){
                        f[i]=f[j]+1;
                        g[i]=j;//存取当前数据下标
                    }
                }
            }
        }
        int k=1;
        for(int i=2;i<=n;i++){
            if(f[k]<f[i]) k = i; //找到最长的子序列的终止下标
        }
        
        cout<<f[k]<<endl;
        
        for(int i=0,len=f[k];i<len;i++){ //从后往前打印
            cout<<a[k]<<" ";
            k=g[k];
        }
        
    } 
    

    最长公共子序列

    给定两个长度分别为N和M的字符串A和B,求既是A的子序列又是B的子序列的字符串长度最长是多少。
    输入格式
    第一行包含两个整数N和M。
    第二行包含一个长度为N的字符串,表示字符串A。
    第三行包含一个长度为M的字符串,表示字符串B。
    字符串均由小写字母构成。
    输出格式
    输出一个整数,表示最大长度。

    分析:

    • 状态表示f[i,j]

    集合:所有在第一个序列的前i个字母中出现(a[i]),且在第二个序列的前j个字母中出现的子序列(b[j])
    属性:Max

    • 状态计算
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    const int N = 1010;
    
    int n,m;
    char a[N],b[N];
    int f[N][N];
    
    int main()
    {
        scanf("%d%d",&n,&m);
        scanf("%s%s",a+1,b+1);
        
        
        /*
        4 5
        acbd
        
        abedc
        
        */
        
        for(int i = 1;i <= n; i++ )
        {
            for(int j = 1;j <= m; j++)
            {
                f[i][j] = max(f[i-1][j],f[i][j-1]);
                if(a[i] == b[j]) f[i][j] = max(f[i][j],f[i-1][j-1]+1);
            }
        }
        printf("%d
    ",f[n][m]);
        
        return 0;
    }
    
    

    石子合并

    设有N堆石子排成一排,其编号为1,2,3,…,N。
    每堆石子有一定的质量,可以用一个整数来描述,现在要将这N堆石子合并成为一堆。
    每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
    例如有4堆石子分别为 1 3 5 2, 我们可以先合并1、2堆,代价为4,得到4 5 2, 又合并 1,2堆,代价为9,得到9 2 ,再合并得到11,总代价为4+9+11=24;
    如果第二步是先合并2,3堆,则代价为7,得到4 7,最后一次合并代价为11,总代价为4+7+11=22。
    问题是:找出一种合理的方法,使总的代价最小,输出最小代价。
    输入格式
    第一行一个数N表示石子的堆数N。
    第二行N个数,表示每堆石子的质量(均不超过1000)。
    输出格式
    输出一个整数,表示最小代价。

    分析:

    • 状态表示f[i,j]

    集合:所有将第i堆石子到第j堆石子合并成一堆石子的合并方式
    属性:Min

    • 状态计算
    #include<iostream>
    
    using namespace std;
    
    const int N = 310;
    
    int n;
    int s[N],f[N][N];
    
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++) cin>>s[i];
        for(int i=1;i<=n;i++) s[i]+=s[i-1];
        
        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 k = l;k<r;k++)
                {
                    f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
                }
            }
        }
        cout<<f[1][n];
    }
    
  • 相关阅读:
    泛型类,泛型方法的使用
    Mapper注解与MapperScan注解
    Configuration注解
    LA 4254 Processor (二分 + 贪心)
    UVa 10382 Watering Grass (贪心 区间覆盖)
    UVA 10795 A Different Task (递归)
    LA 3401 Colored Cubes (搜索 + 暴力)
    uva11464 Even Parity (枚举+递推)
    icpc2021 昆明 K (dfs爆搜)
    hdu3533 (bfs + 模拟)
  • 原文地址:https://www.cnblogs.com/bingers/p/13513733.html
Copyright © 2011-2022 走看看