【算法专项】动态规划专项四

本文深入探讨动态规划的应用,通过两道经典题目“加括号求最大值”与“猴子快乐”,详细阐述如何运用动态规划解决问题的过程及核心思路。文章不仅提供了清晰的解题步骤,还附带了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态规划三--------点击此处
动态规划二--------点击此处
动态规划一--------点击此处

动态规划(dp)核心原理是分类加法原理和分布乘法原理。,在当状态的上一个或几个状态中找出最优解求得当前状态,而对于前一个或者几个状态采用同样的方法直到求出边界状态。

一定要满足下面几点:
1.具有相同子问题。可以分解出几个子问题。
2.满足最优化原理(最优子结构)一个最优决策。
3.具有无后效性:要求每一个问题的决策不能够对解决其它未来的问题产生影响,如果产生影响那么无法保证其最优性。

下面以2道有一定难度的题目进行讲解。

加括号求最大值

题目描述:给你一个表达式,你可以通过在不同的地方添加括号从而改变式子的结果。比如1 + 2 * 3,如果是(1+2)3结果是9,如果是1+23,结果是7,现在给你一个这样的式子,保证只有乘法和加法,但是也许会出现负数,求出这个式子通过不同的加括号方式,所能求得的最大结果。
输入:
多组数据,每组数据第一行给出一个整数N(2<=N<=100),是所给式子的整数个数,下面包含表达式,所有整数和符号之间都会相隔一个空格。
输出:
对于每组测试数据输出一个给出式子能算出的最大值。

样式输入:
4
1 + 2 * 3 + -1
样式输出:
8

我们怎么思考这个问题呢?以下均是我个人理解。
首先肯定要遍历从第一个数到第n个数的,那么设一个函数Max(i,j)表示第i个整数到第j个整数加上一层括号的最大值。那么其实我们需要解决的就是子式的最大值了。我们知道正数乘正数表达式是最大的,所以有效利用好这个乘号。
当然题目要求只有加法和乘法,那其实稍微简单了,我们只要选出乘号,并标记为1,那么加号自然就是0了。注意到,有n个数,那么必然有n-1个符号,那么我们将符号和数字的顺序绑定一起,把初始工作完成。
那么有个疑惑,就是如何保存已搜索的信息,那么设立一个结构体,里面要包含最大值和最小值,表示的就是第i个数到第j个数的情况,有人要问了为什么要保存最小值呢?便于更好地区分,其实加不加也无所谓。
那么子问题是什么呢,就是i到j的情况。通过设立一个中转变量来处理好括号的存在。如果前面小了继续向上推,直到这个中转变量到达了j处(就是最后的n)
下面附代码:

#include <iostream>
#include <cstdio>

#define SUM 101
#define Max 100000000
#define Min -100000000

using namespace std;

int n,num[SUM],ope[SUM];//ope表示存放运算符,num存放数

struct rem{
    int max;
    int min;
};

rem result[SUM][SUM];

rem cmp(int a1,int a2,int a3,int a4){
    int max=Min,min=Max,temp=0;
    if((temp=a1*a3)>max) max=temp;
    if(temp<min) min=temp;
    if((temp=a1*a4)>max) max=temp;
    if(temp<min) min=temp;
    if((temp=a2*a3)>max) max=temp;
    if(temp<min) min=temp;
    if((temp=a2*a4)>max) max=temp;
    if(temp<min) min=temp;
    rem p;
    p.max=max;
    p.min=min;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
    return p;
}

//max[i][j]表示第i个整数到第j个整数加上括号得到的最大值,这样最终结果就是MAX[1][n]
rem MAX(int i,int j){
    if(i==j){
        result[i][j].max=num[i];
        result[i][j].min=num[i];
        return result[i][j];
    }
    if(i<j){
        if(result[i][j].max!=Min){
            return result[i][j];
        }else{
            for(int t=i;t<j;t++){
                rem tempa=MAX(i,t);
                rem tempb=MAX(t+1,j);
                if(ope[t]==1){
                    rem c=cmp(tempa.max,tempa.min,tempb.max,tempb.min);
                    if(c.max>result[i][j].max) result[i][j].max=c.max;
                    if(c.min<result[i][j].min) result[i][j].min=c.min;
                }else{
                    int mm=tempa.max+tempb.max;
                    if(mm>result[i][j].max) result[i][j].max=mm;
                    int mi=tempa.min+tempb.min;
                    if(mi<result[i][j].min) result[i][j].min=mi;
                }
            }
            return result[i][j];
        }
    }
    //return result[i][j];
}

int main(){
    int i,j;
    char ch;
    while(cin>>n){
        for(i=1;i<n;i++){
            cin>>num[i];
            getchar();
            cin>>ch;
            if(ch=='*') ope[i]=1;
            else ope[i]=0;
        }
        cin>>num[n];

        for(i=0;i<=n;i++){
            for(j=0;j<=n;j++){
                result[i][j].max=Min;
                result[i][j].min=Max;
            }
        }
        int max=MAX(1,n).max;
        cout<<max<<endl;
    }
    return 0;
}

猴子快乐

题目描述:在hphp还没有电脑的时候,手机是她唯一的娱乐工具,最喜欢玩的游戏就是猴子,猴子的行动范围是在n(n<=100)根水平排列的柱子的底端上,并且以坐虫子为快乐。每坐到一个虫子他就快乐一点。可是他的快乐是有极限的,因为他只能水平的在相邻的柱子间移动,并且移动一次时间是1s,如果在时间t(0<=t<=10000)猴子刚好在柱子m(1<=m<=n)上并且此时m上恰好会出现一只虫子,那么猴子就可以坐到它了。现在猴子最初(0s)在1号柱子上,在t时间m柱子上是否能有虫子以一个矩阵给出。猴子想要最快乐,你是否能够帮助它呢~
输入:
多组测试数据,每组测试数据首先给出整数n,t (0<n<=100,0<=t<=10000)占一行。n表示总共有多少柱子,t表示游戏结束的时间,t时间猴子就不可以再坐虫子咯。接下来有n行,每行有t个整数(0或1)T0,T2…Tt-1。第i行第j个数字表示第i个柱子在j时间是否有虫子出现。
输出:
对于每组测试数据输出一个整数,表示猴子快乐的最大点数。

样式输入:
3 4
0 1 0 1
1 0 0 1
1 1 1 1
3 4
1 0 1 0
1 1 1 0
1 1 1 1
1 5
1 0 1 0 1
样式输出:
2
2
3

分析:用max[i][j]表示在第i时间在第j个柱子上时能得到的最大的幸福值;grid[i][j]表示第i时间第j柱子是否有虫子。那么mx[i][j]=Max(mx[i-1][j-1],m[i-1[j],mx[i-1][j+1])+grid[j][i].边界问题要特别注意。

#include <iostream>

#define MAX 1<<29
#define N 100
#define T 10100

using namespace std;

int mx[T][N],grid[N][T];

int n,limt;

int Max(int a,int b,int c){
    int z;
    z=(a>b)?a:b;
    return z>c?z:c;
}

int main(){
    while(cin>>n>>limt){
        for(int i=1;i<=n;i++){
            for(int j=0;j<limt;j++){
                cin>>grid[i][j];
            }
        }

        for(int i=1;i<=n;i++){
            for(int j=0;j<limt;j++){
                mx[j][i]=-MAX;
            }
        }

        mx[0][1]=grid[1][0];
        for(int i=1;i<limt;i++){
            for(int j=1;j<=n&&j<=i+1;j++){
                int a=-MAX,b=-MAX,c=-MAX;
                if(j>1) a=mx[i-1][j-1];
                b=mx[i-1][j];
                if(j<n) c=mx[i-1][j+1];
                mx[i][j]=Max(a,b,c)+grid[j][i];
            }
        }

        int mxmx=0;
        for(int i=1;i<=n;i++){
            if(mx[limt-1][i]>mxmx) mxmx=mx[limt-1][i];
        }
        cout<<mxmx<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值