还是先简单的介绍一下动态规划的算法思想,跟分治法的思想相似的是把一个比较大的问题分解成若干子问题,而分治法分解出来的子问题都是相同规模有相同的解决办法的,动态规划可以通过空间换时间来解决这些相同的问题,比如说我们计算10的4次方的问题,可以分解为10^2*10^2,我们计算出了第一个10的方时可以把答案记录下来再计算到第二个10的方时,直接调用就好了。
因此,动态规划算法适合解决的问题有以下特点:
1.最优子结构性质(也就是说当问题分解的时候无论怎么分解,由主问题的最优性总能推出子问题的最优性)
2.无后效性(强调历史的任何状态不会影响未来的决策)
3.子问题重叠性(子问题有重叠可以归集解决)
动态规划算法的步骤有以下三个步骤:找出最优子结构性质——找到递归求解的方法——自顶向上计算出最优解
动态规划解决的比较典型的问题就是矩阵的连乘和多边形游戏问题。
多边形游戏问题描述:开始时先构建一个n个顶点的多边形,每个顶点赋予一个整数值,每个边赋予一个运算符(+或者*),所有边依次用1到n进行编号。游戏第一步将一条边删除,随后n-1步按照如下操作:选择一条边和两个顶点,计算出结果用结果作为数值生成一个新的顶点替代这条边和两个顶点,直至所有顶点被删除,求对于任意给定的多边形,计算得到的最大的结果。
(图片来自CNblog)
算法思路:多边形顶点边序列为op[i]和v[i]其中op为运算符,v为数值,op为运算符,假设最后一次合并运算发生在op[i+s],可以在op[i+s]处将链分为p(i,s)和p(i+s,j-s)。m1为子链p(i,s)的任意一种合并方式得到的值,而a,b为合并过程中可能得到的最小值和最大值即:a = m[i,i+s,0],b = m[i,i+s,1] 具体定义可见代码注释,可知a<=m1<=b。同理定义c和d,分情况考虑op[i,s] = '+' || '*' 时的最大值最小值,minf与maxf。最后在断开位置上调用循环。代码注释很详细...
代码如下:
#include <iostream>
using namespace std;
const int MAX = 101;
int N,m[MAX][MAX][2];
//m[i,j,0]为(i,j)链合并得出的最小值
//m[i,j,1](i,j)链合并得出的最大值
int a[MAX];
char op[MAX];//用于存放运算符
//n为多边形的边数
//i,j分别代表顶点和边
//s表示最优的断开位置
//minf表示s处断开的最小值
//maxf表示s处断开的最大值
void MinMax(int n, int i, int s, int j, int& minf, int& maxf){
int e[5];
int r=(i+s-1)%n+1; //跟循环栈一样的编号方法
int a=m[i][s][0], b=m[i][s][1], c=m[r][j-s][0], d=m[r][j-s][1];
//当op[i+s]=’+’时m[i,j,0]=a+c ;m[i,j,1]=b+d
if(op[r] == '+'){
minf = a+c;
maxf = b+d;
} else {
e[1]=a*c;
e[2]=a*d;
e[3]=b*c;
e[4]=b*d;
minf=e[1];
maxf=e[1];
//当op[i+s]=’*’时,m[i,j,0]=min{ac,ad,bc,bd},m[i,j,1]=max{ac,ad,bc,bd}
for(int m=2; m<5; m++){
if(minf > e[m])
minf = e[m];
if(minf < e[m])
maxf = e[m];
}
}
}
int PolyMax(int n, int& p){
int minf, maxf;
//三层循环分别代表链得长度,删除的边的编号,断开的点
for(int j=2; j <= n; j++){
for(int i=1; i <= n; i++){
for(int s=1; s < j; s++){
MinMax(n, i, s, j, minf, maxf);
if(m[i][j][0] > minf)
m[i][j][0] = minf;
if(m[i][j][1] < maxf)
m[i][j][1] = maxf;
}
}
}
int temp=m[1][n][1];
p=1;
for(int i=2 ;i<=n; i++){
if(temp<m[i][n][1]){
temp=m[i][n][1];
p=i;
}
}
return temp;
}
int main(){
int p;
cout <<"请输入多边形顶点数:"<<endl;
cin >>N;
for(int i=1; i<=N; i++){
cout <<"请输入多边形顶点:"<<i<<"数值为"<<endl;
cin >>a[i];
m[i][1][0]=a[i];
m[i][1][1]=a[i];
cout <<"请输入多边形边:"<<i<<"运算符为(只限+和*)"<<endl;
cin >>op[i];
}
cout <<"多边形首次删除第"<<p<<"条边,结果为:"<<PolyMax(N,p)<<endl;
return 0;
}
程序运行效果图:
时间复杂度:多边形游戏问题的时间复杂度取决于PolyMax(),三层循环易知时间复杂度O(N^3)。