动态规划三--------点击此处
动态规划二--------点击此处
动态规划一--------点击此处
动态规划(dp)核心原理是分类加法原理和分布乘法原理。,在当状态的上一个或几个状态中找出最优解求得当前状态,而对于前一个或者几个状态采用同样的方法直到求出边界状态。
一定要满足下面几点:
1.具有相同子问题。可以分解出几个子问题。
2.满足最优化原理(最优子结构)一个最优决策。
3.具有无后效性:要求每一个问题的决策不能够对解决其它未来的问题产生影响,如果产生影响那么无法保证其最优性。
下面以2道有一定难度的题目进行讲解。
目录
加括号求最大值
我们怎么思考这个问题呢?以下均是我个人理解。
首先肯定要遍历从第一个数到第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;
}
猴子快乐
分析:用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;
}