zoukankan      html  css  js  c++  java
  • 算法-动规

    例子1 数字三角形

    #include <iostream> 
    #include <algorithm>
    #define MAX 101 
    using namespace std;
    //数字三角形 从顶端到底边数字之和最大 求最大和
    int D[MAX][MAX]; //存放数字三角形
    int n;
    int MAXsum(int i, int j){
        //从第i行dij个数字往下找,最大的和
        if(i==n){
            //最后一行
            return D[i][j]; 
        }
        int x = Maxsum(i+1,j);
        int y = Maxsum(i+1,j+1);
        return D[i][j]+max(x,y);
    }
    int main(){
        int i,j;
        cin>>n;
        for(i=1;i<=n;j++){
            for(j=1;j<=i;j++){
                cin>>D[i][j];
            }
        }
        cout<<Maxsum(1,1);
    } 

    改进后:

    #include <iostream> 
    #include <algorithm>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //数字三角形 从顶端到底边数字之和最大 求最大和
    int D[MAX][MAX]; //存放数字三角形
    int n;
    int R[MAX][MAX];
    
    int Maxsum(int i, int j){
        //从第i行dij个数字往下找,最大的和
        if(i==n){
            //最后一行
            return D[i][j]; 
        }
        if(R[i][j]!=-1){
            return R[i][j];
        }
        int x = Maxsum(i+1,j);
        int y = Maxsum(i+1,j+1);
        R[i][j]= D[i][j]+max(x,y);
        return R[i][j];
    }
    int main(){
        int i,j;
        cin>>n;
        for(i=1;i<=n;i++){
            for(j=1;j<=i;j++){
                cin>>D[i][j];
                R[i][j]=-1;
            }
        }
        cout<<Maxsum(1,1);
    } 

    改为从最后一行往上递推

    #include <iostream> 
    #include <algorithm>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //数字三角形 从顶端到底边数字之和最大 求最大和
    int D[MAX][MAX]; //存放数字三角形
    int n;
    int R[MAX][MAX];
    //从最后一行往上推
     
    int main(){
        int i,j;
        cin>>n;
        for(i=1;i<=n;i++){
            for(j=1;j<=i;j++){
                cin>>D[i][j];
                R[i][j]=0;
            }
        }
        //拷贝三角形的最后一行到R 代表最后一行往下找最大和就是每个元素本身 
        for(int i=1;i<=n;i++){
            R[n][i]= D[n][i]; 
        }
        for(int i=n-1;i>=1;i--){ //由倒数第二行往上一层一层填满R 
            for(int j=1;j<=i;j++){ //每一行从前到后 
                R[i][j]=max(R[i+1][j],R[i+1][j+1])+D[i][j];
            }
        }
        cout<<R[1][1];
    } 

    空间优化

    #include <iostream> 
    #include <algorithm>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //数字三角形 从顶端到底边数字之和最大 求最大和
    int D[MAX][MAX]; //存放数字三角形
    int n;
    int R[MAX];
    //从最后一行往上推
     
    int main(){
        int i,j;
        cin>>n;
        for(i=1;i<=n;i++){
            R[i]=0;
            for(j=1;j<=i;j++){
                cin>>D[i][j];
            }
        }
        //拷贝三角形的最后一行到R 代表最后一行往下找最大和就是每个元素本身 
        for(int i=1;i<=n;i++){
            R[i]= D[n][i]; 
        }
        for(int i=n-1;i>=1;i--){ //由倒数第二行往上一层一层填满R 
            for(int j=1;j<=i;j++){ //每一行从前到后 
                R[j]=max(R[j],R[j+1])+D[i][j];
            }
        }
        cout<<R[1];
    } 

    空间再次优化

    #include <iostream> 
    #include <algorithm>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //数字三角形 从顶端到底边数字之和最大 求最大和
    int D[MAX][MAX]; //存放数字三角形
    int n;
    //从最后一行往上推
     
    int main(){
        int i,j;
        cin>>n;
        for(i=1;i<=n;i++){
            for(j=1;j<=i;j++){
                cin>>D[i][j];
            }
        }
    
        for(int i=n-1;i>=1;i--){ //由倒数第二行往上一层一层填满R 
            for(int j=1;j<=i;j++){ //每一行从前到后 
                D[n][j]=max(D[n][j],D[n][j+1])+D[i][j];
            }
        }
        cout<<D[n][1];
    } 

    例子2 最长上升子序列

    #include <iostream> 
    #include <algorithm>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //动规问题解题方式
    //将原问题分解为子问题 子问题要与原问题相似 并且规模变小 
    //确定状态 以及确定状态需要几个变量
     //确定初始状态的值 其他状态由这些初始状态开始求得
    //确认状态转移方程 即如何从已知状态推出其他状态的值 
    //动规能解决的问题的特点是 问题具有最优子结构和无后效性
     
     //最长上升子序列  上升子序列是一个序列里值逐渐递增的非连续子序列 
     // 对于给定序列,求最长上升子序列 
     
     //子问题: 以a(k)为终点的最长上升子序列的长度 将原问题分解成了n(序列长度)个子问题
     //只需要从这n个子问题中的答案中,选取最大的
     //状态 元素a(k)的下标k 第k个元素
     //初始状态 k=1 时,值为1 状态转移方程 遍历序列 1-k之间的所有元素,满足条件:
     //1. a(i)< a(k)  2.i对应的状态值是1-k中最大的 那么a(k)= a(i)+1 否则a(k)=1; 
     
    const int MAXN=1010;
    int a[MAXN];//存放序列
    int Maxlen[MAXN];// Maxlen(i)存放以a(i)为终点的最长上升子序列的序列长度 
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        Maxlen[1]=1;//初始状态
        int MAXLEN=1;
        for(int i=2;i<=n;i++){
            int maxlen=1;
            for(int j=1;j<i;j++){
                if(a[j]<a[i]&& Maxlen[j]>maxlen){
                    maxlen=Maxlen[j];
                }
                Maxlen[i]=maxlen+1;
            }
            if(Maxlen[i]>MAXLEN){
                MAXLEN = Maxlen[i];
            }
        }
        cout<<MAXLEN<<endl; 
        
    } 

    人人为我型

    #include <iostream> 
    #include <algorithm>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //动规问题解题方式
    //将原问题分解为子问题 子问题要与原问题相似 并且规模变小 
    //确定状态 以及确定状态需要几个变量
     //确定初始状态的值 其他状态由这些初始状态开始求得
    //确认状态转移方程 即如何从已知状态推出其他状态的值 
    //动规能解决的问题的特点是 问题具有最优子结构和无后效性
     
     //最长上升子序列  上升子序列是一个序列里值逐渐递增的非连续子序列 
     // 对于给定序列,求最长上升子序列 
     
     //子问题: 以a(k)为终点的最长上升子序列的长度 将原问题分解成了n(序列长度)个子问题
     //只需要从这n个子问题中的答案中,选取最大的
     //状态 元素a(k)的下标k 第k个元素
     //初始状态 k=1 时,值为1 状态转移方程 遍历序列 1-k之间的所有元素,满足条件:
     //1. a(i)< a(k)  2.i对应的状态值是1-k中最大的 那么a(k)= a(i)+1 否则a(k)=1; 
     
    const int MAXN=1010;
    int a[MAXN];//存放序列
    int Maxlen[MAXN];// Maxlen(i)存放以a(i)为终点的最长上升子序列的序列长度 
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            Maxlen[i]=0;
        }
        Maxlen[1]=1;
        for(int i=1;i<=n;i++){ //往后更新 
            for(int j=i+1;j<=n;j++){
                if(a[j]>a[i]){
                    Maxlen[j]=Maxlen[i]+1;
                }
    
            }
        }
        int maxlen=1;
        for(int i=1;i<=n;i++){
            if(Maxlen[i]>maxlen){
                maxlen=Maxlen[i];
            }
        }
        cout<<maxlen<<endl; 
        
    } 

    例子3 最长公共子序列

    #include <iostream> 
    #include <algorithm>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //最长公共子序列
     //给定两个字符串,求出最长公共子序列的长度,
     //子序列中的每个字符都能在两个原串中找到 并且每个字符的先后顺序和原串中一致 
     //子问题:两个字符串左边的i 和 j 个字符构成的字符串的最长公共子序列长度 Maxlen(i,j) 
     //状态 i和j
     //初始状态  Maxlen(0,n) = Maxlen(n,0)=0 ; 
     
     string s1,s2;
     int MAXLEN[100][100];
     int Maxlen(int i,int j){
         if(i==0 || j==0){
             return 0;
        }
        if(MAXLEN[i][j]!=-1){
            return MAXLEN[i][j];
        }
        if(s1[i-1]==s2[j-1]){
            MAXLEN[i][j]=Maxlen(i-1,j-1)+1;
        }
        else{
            MAXLEN[i][j]=max(Maxlen(i,j-1),Maxlen(i-1,j));
        }
        return MAXLEN[i][j];
     }
    int main(){
        memset(MAXLEN,0xff,sizeof(MAXLEN));
        cin>>s1>>s2;
        int len1=s1.size();
        int len2=s2.size();
        cout<<Maxlen(len1,len2);
        
    } 

    例子4 最佳加法表达式

    #include <iostream> 
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //最佳加法表达式
    //由1-9数字组成的数字串,将m个加号插入到数字串中,在可能形成的表达式中,最小的表达式的值
    //子问题: V(0,n)=n个数字构成的整数 状态转移函数V(m,n)=V(m-1,i)+Num(i+1,n) 在i之后插入最后一个加号,遍历选出最小的 
    string s;
    int num[100][100];
    int NUM(int i,int j){
        //string[i] 到string[j]之间的字符串转换为整数 不包括string j 
        if(num[i][j]!=-1){
            return num[i][j];
        }
        int len=j-i;//位数
        int result=0;
        for(int r=len-1;r>=0;r--){
            result += (s[i]-'0')*pow(10,r);
            i++; 
        }
        num[i][j]=result;
        return num[i][j];
    }
    int v[100][100];
    int V(int m,int n){
        //将m个加号插到n个数字串中
        int r=1<<30; 
        if(m==0){
            return NUM(0,n);
        } 
        if(m>n-1){
            return r;
        }
        if(v[m][n]!=-1){
            return v[m][n];
        }
        for(int i=m;i<=n-1;i++){  //将m-1个加号插到i个数字之中,i从m开始到n-1 
            r = min(r,V(m-1,i)+NUM(i,n));
        }
        v[m][n]=r;
        return r;
    }
    int main(){
        memset(num,0xff,sizeof(num));
        memset(v,0xff,sizeof(v));
        cin>>s;
        int m;
        cin>>m;
        cout<<V(m,s.size());    
    } 

    例子5 神奇的口袋

    #include <iostream> 
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //神奇的口袋
    //n个物品 有不同的体积 凑成40 的凑法
    int goods[100]; 
    int WAYS[100][100];
    int ways(int w,int k){
        //从前k种物品中选择 凑成总值为w 
        if(w==0){
            return 1;
        }
        if(k<=0){
            return 0;
        }
        if(WAYS[w][k]!=-1){
            return WAYS[w][k];
        }
        WAYS[w][k]=ways(w,k-1)+ways(w-goods[k],k-1);
        return WAYS[w][k];
    } 
    
    int main(){
        memset(WAYS,0xff,sizeof(WAYS));
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>goods[i];
        }
        cout<<ways(40,n);
    } 

    动规解法

    #include <iostream> 
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //神奇的口袋
    //n个物品 有不同的体积 凑成指定值 的凑法
    int goods[100]; 
    int ways[100][100]; //ways[i][j] 前j种物品种凑出i 
    
    int main(){
        memset(ways,0,sizeof(ways));
        int n; //n件物品 
        cin>>n;
        int r;//目标值 
        cin>>r;
        for(int i=1;i<=n;i++){
            cin>>goods[i];
            ways[0][i]= 1; //边界状态 
        }
        ways[0][0]=1;
        for(int w=1;w<=r;w++){
            for(int k=1;k<=n;k++){
                ways[w][k]=ways[w][k-1];   
                if(w-goods[k]>=0){ //如果有可能取第k件物品 
                    ways[w][k] += ways[w-goods[k]][k-1];
                }
            }
        }
        cout<<ways[r][n];
    } 

    我为人人解法

    #include <iostream> 
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //神奇的口袋
    //n个物品 有不同的体积 凑成指定值 的凑法
    //我为人人解法
     
    int main(){
        int MaxSum[100]; 
        memset(MaxSum,0,sizeof(MaxSum));
        int n; //n件物品 
        cin>>n;
        int r;//目标值 
        cin>>r;
        int input;
        for(int i=1;i<=n;i++){
            cin>>input; 
            for(int j=r;j>=1;j--){  //由后往前遍历 因为可能要用到后面的值 
                if(MaxSum[j]>0 && j+input<=r){  //如果有sum[j]种方法可以凑成j,那么多了input,凑成j+input的方法就多了 sum[j]种 
                    MaxSum[j+input] += MaxSum[j];
                }
            }
            MaxSum[input]++; 
        }
    
        cout<<MaxSum[r];
    }

     例子6 背包问题

    #include <iostream> 
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //背包问题
    //N件物品 和 容积为M 的书包,第i件物品的体积w[i] 价值为d[i]
    //求解将哪些物品装入背包可使价值总和最大  
    //状态: F[i][j]表示取前i种物品,使它们总体积不超过j的最优取法的价值总和
    //边界 如果第一件物品的体积小于j 那么F[1][j]=d[1] 否则F[1][j]=0 
    //状态转移方程 取或者不取第i件物品 取最大值 
     
     
    int main(){
        int N,M;
        cin>>N>>M;  //N<100 M<100
        int w[100];
        int d[100];
        int F[100][100]; 
        memset(F,0,sizeof(F));
        for(int i=1;i<=N;i++){
            cin>>w[i]>>d[i];
        }
        for(int j=1;j<=M;j++){ //遍历所有体积 
            if(w[1]<=j){  //边界条件 从前1种物品中取 使得总体积不超过j 的价值总共和 
                F[1][j]=d[1];
            }
            else{
                F[1][j]=0;
            }
        }
        for(int i=2;i<=N;i++){ //遍历剩下的物品 
            for(int j=1;j<=M;j++){ //遍历剩下的体积 
                 if(j-w[i]>=0){
                     F[i][j]=max(F[i-1][j],F[i-1][j-w[i]]+d[i]);
                 }
                 else{
                     F[i][j]=F[i-1][j];
                 }
            }
        }
        cout<<F[N][M];
    
    }

    节省空间 滚动一维数组解法

    #include <iostream> 
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #define MAX 101 
    using namespace std;
    //背包问题
    //N件物品 和 容积为M 的书包,第i件物品的体积w[i] 价值为d[i]
    //求解将哪些物品装入背包可使价值总和最大  
    //状态: F[i][j]表示取前i种物品,使它们总体积不超过j的最优取法的价值总和
    //边界 如果第一件物品的体积小于j 那么F[1][j]=d[1] 否则F[1][j]=0 
    //状态转移方程 取或者不取第i件物品 取最大值 
     
    //观察到状态转移方程只使用了上一行的数组 因此改变遍历方向即可 缩减为1维数组 
    int main(){
        int N,M;
        cin>>N>>M;  //N<100 M<100
        int w[100];
        int d[100];
        int F[100]; 
        memset(F,0,sizeof(F));
        for(int i=1;i<=N;i++){
            cin>>w[i]>>d[i];
        }
        for(int j=1;j<=M;j++){ //遍历所有体积 
            if(w[1]<=j){  //边界条件 从前1种物品中取 使得总体积不超过j 的价值总共和 
                F[j]=d[1];
            }
            else{
                F[j]=0;
            }
        }
        for(int i=2;i<=N;i++){ //遍历剩下的物品 
            for(int j=M;j>=1;j--){ //遍历剩下的体积 
                 if(j-w[i]>=0){
                     F[j]=max(F[j],F[j-w[i]]+d[i]);
                 }
            }
        }
        cout<<F[M];
    
    }

    例子7 滑雪问题

    #include <iostream> 
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    using namespace std;
    //滑雪问题
    // 二维数组给出一个区域高度
    //一个人可以从这个点滑向上下左右相邻的四个点之一
    //求最长区域的长度 (往下滑了多少个点) 
    //状态:L(i,j)从 点(i,j)点出发的滑行长度
    //一个点(i,j)如果周围没有比它低的点,L(i,j)=1
    //否则L(i,j)等于周围四个点的中 比(i,j)低,且L值最大的点的L值+1
    //知道一个点必须要周围四个点的L值
    //用递归做
    
    int M[200][200];//区域高度
    int l[200][200];//l[i][j]代表(i,j)点出发的最长滑行长度 
    int R,C;//R行 C列 
    int L(int i,int j){
        if(M[i][j] <= M[i-1][j] && M[i][j] <= M[i+1][j] && M[i][j] <= M[i][j-1] && M[i][j] <=M[i][j+1]){
            return 1;
        }
        if(l[i][j]!=-1){
            return l[i][j];
        }
        int result=1;
        if(M[i][j] >M[i-1][j]) 
            result = max(result,L(i-1,j)+1);
        if(M[i][j] >M[i+1][j])
            result = max(result,L(i+1,j)+1);
        if(M[i][j] >M[i][j+1])
            result = max(result,L(i,j+1)+1);
        if(M[i][j]> M[i][j-1])
            result = max(result,L(i,j-1)+1);
        l[i][j]=result;
        return l[i][j];
    } 
    int main(){
        cin>>R>>C;
        memset(l,0xff,sizeof(l));
        int INFINITE = 1<<30;
        for(int i=1;i<=R;i++){
            for(int r=1;r<=C;r++){
                cin>>M[i][r];
            }
            M[i][0]=INFINITE;//边界初始化 
            M[i][C+1]=INFINITE;
        }
        for(int i=1;i<=C;i++){
            M[0][i]=INFINITE;
            M[R+1][i]=INFINITE;
        }
        int MAX=1;
        for(int i=1;i<=R;i++){
            for(int j=1;j<=C;j++){
                MAX=max(MAX,L(i,j));
            }
        }
        cout<<MAX;
    }
  • 相关阅读:
    洛谷八连测R7 nzhtl1477-我回来了
    String中的equals方法原理分析
    Java线程
    Spring配置日志级别报红:Caused by: org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'logging.level' to java.util.Map<java.lang.String
    # SpringMVC跨服务器上传文件出现的问题
    使用Maven创建Spring-web项目的基本流程
    Maven的下载与安装(环境变量的配置)
    eNSP的安装(附链接)
    数据库分页操作
    Sql语句占位符?的使用
  • 原文地址:https://www.cnblogs.com/Latticeeee/p/9070176.html
Copyright © 2011-2022 走看看