在学习过程中,遇到动态规划问题。查了很多的资料,终于会一点点了。
动态规划一般思路:
1 将原问题转化成子问题求解。
蒋原问题转换成若干个子问题,子问题和原问题相同或相似,但是规模会小,子问题解决,原问题便解决。
子问题求出后便保存,只求一次。子问题解决,原问题即解决。
2 确定状态
这一段是copy来的;
- 在用动态规划解题时,我们往往将和子问题相关的各个变量的一组取值,称之为一个“状 态”。一个“状态”对应于一个或多个子问题, 所谓某个“状态”下的“值”,就是这个“状 态”所对应的子问题的解。
- 所有“状态”的集合,构成问题的“状态空间”。“状态空间”的大小,与用动态规划解决问题的时间复杂度直接相关。
我的理解就是状态就是表示每个子问题的值得集合。用来表示和确定每个子问题。
3 确定初始状态和边界状态。
4 确定状态转移方程。(重点与难点)
从一个状态推出下一个状态的递推方程,便是状态转移方程。这个靠经验还有猜。
例题:
数字三角形(POJ1163)
在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99
输入格式:
5 //表示三角形的行数 接下来输入三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
要求输出最大和:
可以用简单的递归来写这个程序:
#include <iostream>
#include <algorithm>
using namespace std;
int D[101][101];
int n;
int max101][101];
int Max(int i, int j){
if( max[i][j] != -1 ) {
return max[i][j];
}
if(i==n){
max[i][j] = D[i][j];
}
else{
int x = Max(i+1,j);
int y = Max(i+1,j+1);
max[i][j] = max(x,y)+ D[i][j];
}
return max[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];
max[i][j] = -1;
}
}
cout << Max(1,1) << endl;
}
但是这样做会超时,因为重复计算太多了。但是我们可以把每一次,max都记录下来,这样就可以节省大量的时间。
#include <iostream>
#include <algorithm>
using namespace std;
int D[101][101];
int n;
int Max(int i, int j){
if(i==n){
return D[i][j];
}
else {
int x = Max(i+1,j);
int y = Max(i+1,j+1);
return max(x,y)+D[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];
}
}
cout << Max(1,1) << endl;
}
也可以用动态规划的方法来写:
动态转移方程:
max[i][j] = max(max[i+1][j],max[i+1][j+1]) + D[i][j];
i--;j++;
代码:
#include <iostream>
#include <algorithm>
using namespace std;
int D[101][101];
int n;
int max[101][101];
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 = 1;i <= n; ++ i ) {
max[n][i] = D[n][i];
}
for( int i = n-1; i>= 1; --i ){
for( int j = 1; j <= i; ++j ){
max[i][j] = max(max[i+1][j],max[i+1][j+1]) + D[i][j];
}
}
cout << max[1][1] << endl;
}