首先,我们看一下这道题
输入格式:
5 //表示三角形的行数 接下来输入三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大
我们使用D(i,j)来表示第i行的第j个数
MaxNum(i,j)来表示D(i,j)到底边的各路径中的最佳路径(数字之和最大)
返回的值是
###第一版本:普通递归###
#include <iostream>
#include <algorithm>
#define MAX 101
using namespace std;
int D[MAX][MAX];
int n;
int MaxSum(int i, int j){
if(i==n)
return D[i][j];
int x = MaxSum(i+1,j);
int y = MaxSum(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 << MaxSum(1,1) << endl;
}
Java版递推
public class Main2 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
int n=in.nextInt();
int[][] a = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i; j++) {
a[i][j] = in.nextInt();
}
}
for (int i = n - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
a[i-1][j]+=a[i][j]>a[i][j+1]?a[i][j]:a[i][j+1];
}
}
System.out.println(a[0][0]);
}
}
}
###第二版本:记忆型递归###
记忆型递归就是每次计算后(在遍历赋值)给他做个记号,如果已经被标记说明已经计算过,直接返回。
#include <iostream>
#include <algorithm>
#define MAX 101
using namespace std;
int D[MAX][MAX];
int n;
int MaxNum[MAX][MAX];
int MaxSum(int i, int j){
if(MaxNum[i][j]!=-1)
return MaxSum(i,j);
if(i==n)
return D[i][j];
int x = MaxSum(i+1,j);
int y = MaxSum(i+1,j+1);
cout << max(x,y)+D[i][j] << endl;
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];
MaxNum[i][j]=-1;
}
}
cout << MaxSum(1,1) << endl;
}
##动态规划之背包问题
背包问题具体例子:假设现有容量10kg的背包,另外有3个物品,分别为a1,a2,a3. 物品a1重量为3kg,价值为4;
物品a2重量为4kg,价值为5;物品a3重量为5kg,价值为6,将哪些物品放入背包使背包中的价值最大?
动态规划的思路:先将原始问题一般化,欲求总价值,即求前i个物品放入容器m(kg)背包的最大价值c[i][m]–使用一个数组来存储最大价值,当m取10,i取3时,即原始问题了,而前i个物品放入容量为m的背包,又可以转化成前(i-1)个物品放入背包的问题,下面使用数学表达式描述它们两者之间的具体关系
表达式中的各个符号的具体含义
w[i] : 第i个物体的重量
p[i] : 第i个物品的价值
c[i][m] : 前i个物品放入容量m的背包的最大价值
c[i-1][m-w[i]] : 前i-1个物品放入容量为m-w[i]的背包的最大价值
由此可得
c[i][m]=max{c[i-1][m-w[i]]+p[i],c[i-1][m]}
public class DP {
public static void main(String[] args) {
int m=10;
int n=3;
int[] w={3,4,5};
int[] p={4,5,6};
int[][] c=BackPack_Solution(m,n,w,p);
System.out.println(c[3][10]);
}
/**
* @param m 表示背包的最大容量
* @param n 表示商品个数
* @param w 表示商品重量数组
* @param p 表示商品价值数组
*/
private static int[][] BackPack_Solution(int m, int n, int[] w, int[] p) {
int[][] c=new int[n+1][m+1];
//把二维数组0部分都置0,让从1开始
for(int i=0;i<n+1;i++){
c[i][0]=0;
}
for(int j=0;j<m+1;j++){
c[0][j]=0;
}
//当物品i件重量为j,如果第i件的重量(w[i-1])小于重量j时,c[i][j]为下列两种情况之一
//(1)物品i不放入背包,所以c[i][j]等于c[i-1][j]的值
//(2)物品i放入背包,所以背包剩余的重量为了j-w[i-1],那么c[i][j]等于c[i-1][j-w[i-1]]+p[i-1],
//注意:w[i-1]和p[i-1]是i物品的重量和价值
for(int i=1;i<n+1;i++){
for(int j=1;j<m+1;j++){
if(w[i-1]<=j){
if(c[i-1][j]<c[i-1][j-w[i-1]]+p[i-1]){
c[i][j]=c[i-1][j-w[i-1]]+p[i-1];
}else{
c[i][j]=c[i-1][j];
}
}else{
c[i][j]=c[i-1][j];
}
}
}
return c;
}
}
如果我们有面值为1元、3元和5元的硬币若干枚,如何用最少的硬币凑够11元?
/*
* 我们要对它抽象一下,
d(i)=min{ d(i-vj)+1 },其中i-vj >=0,vj表示第j个硬币的面值;
*/
/*
* 通过追踪我们是如何从前一个状态值得到当前状态值的,可以找到每一次我们用的是什么面值的硬币。
* 比如,从上面的图我们可以看出,最终结果d(11)=d(10)+1(面值为1),而d(10)=d(5)+1(面值为5),
* 最后d(5)=d(0)+1 (面值为5)。所以我们凑够11元最少需要的3枚硬币是:1元、5元、5元。
*/
public class Cent {
public static void main(String[] args) {
int[] a={1,3,5};
int sum=6;
int[] dp=new int[sum+1];
dp[0]=0;
for(int i=0;i<=sum;i++)
dp[i]=i;
for(int i=1;i<=sum;i++){
for(int j=0;j<3;j++){
if(i>=a[j]&&dp[i-a[j]]+1<dp[i]){//对比,去最小值
dp[i]=dp[i-a[j]]+1;
}
}
}
System.out.println(dp[sum]);
}
}