动态规划算法思想:把待求解问题分解成若干个子问题,先求解子问题, 然后由这些子问题的解得到原问题的解,但动态规划求解过的子问题的结果会 被保留下来,不像递归那样每个子问题的求解都要从头开始返回求解。动态规 划求解问题的关键在于获得各个阶段子问题的递推关系式:
(1)分析原问题的最优解性质,刻画其结构特征
(2)递归定义最优值
(3)自底向上(由后向前)的方式计算最优值
(4)根据计算最优值时得到的信息,构造一个最优解。
一、动态规划实现0-1背包问题
public class Knapsack {
public static int knapsack(int[] weight, int[] value,int maxweight){
int n = weight.length;
//最大价值数组为maxvalue[N+1][maxWeight+1],因为我们要从0开始保存
int[][] maxvalue = new int[n+1][maxweight + 1];
//重量和物品为0时,价值为0
for (int i = 0; i < maxweight + 1; i++) {
maxvalue[0][i] = 0;
}
for (int i = 0; i < n + 1; i++) {
maxvalue[i][0] = 0;
}
//i:只拿前i件物品(这里的i因为取了0,所以对应到weight和value里面都是i-1号位置)
//j:假设能取的总重量为j
//n是物品件数
for (int i = 1; i <= n ; i++) {
for (int j = 1; j <= maxweight; j++) {
//当前最大价值等于放上一件的最大价值
maxvalue[i][j] = maxvalue[i-1][j];
//如果当前件的重量小于总重量,可以放进去或者拿出别的东西再放进去
if (weight[i-1] <= j) {
//比较(不放这个物品的价值)和
//(这个物品的价值 加上 当前能放的总重量减去当前物品重量时取前i-1个物品时的对应重量时候的最高价值)
if(maxvalue[i-1][j - weight[i-1]] + value[i-1]>maxvalue[i-1][j]) {
maxvalue[i][j] = maxvalue[i-1][j - weight[i-1]] + value[i-1];
}
}
}
}
return maxvalue[n][maxweight];
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
int weight[] = {11,7,9,12,3,10};
int value[] = {10,5,7,9,2,12};
int maxweight = 8;
System.out.println(knapsack(weight, value, maxweight));
}
}
二、动态规划实现多段图
#include <iostream>
using namespace std;
#define N 12+1 //结点数——为了实现方便,使结点编号和数组下标一致
#define K 5 //段数
#define INF 1000
int c[N][N]; //c[i][j]表示i到j的花费
int cost[N]; //cost[i]表示到结点i的最小花费
int d[N]; //d[i]表示由结点i指向的最小成本边的另一端的结点
int P[K]; //每一阶段最短路径成本
void init();
void fGraph();
int main(int argc, char *argv[])
{
init(); //初始化
fGraph();
int k = K;
int sum = 0;
int n = N - 1;
while (k != 1)//遍历每个阶段的最短路径成本
{
sum += cost[n];
n = d[n];
k--;
}
cout << "最小成本路径为:" << sum<<endl;
return 0;
}
void fGraph()
{
int min;
for (int j = N - 1; j > 0; j--)//向前处理方法
{
min = INF;
//for (int i = 1; i < N; i++)
for (int i = j - 1; i > 0; i--)//从j - 1开始可以减少比较次数
{
if (c[i][j] != INF && cost[j] + c[i][j] < min)//找出结点r, 满足<j, r>∈E且使c(j,r)+COST(r)值最小
{
min = cost[i] + c[i][j];
d[j] = i;
}
}
cost[j] = min;//数组cost[i]保留到结点i的最短边的权值
}
}
void init()
{
for (int i = 1; i < N; i++)
{
cost[i] = 0;
for (int j = 1; j < N; j++)
{
c[i][j] = INF;
}
}
//为了测试方便,直接码出来了
c[1][2] = 9; c[1][3] = 7; c[1][4] = 3; c[1][5] = 2;
c[2][6] = 4; c[2][7] = 2; c[2][8] = 1; c[3][6] = 2;
c[3][7] = 7; c[4][8] = 11; c[5][7] = 11; c[5][8] = 8;
c[6][9] = 6; c[6][10] = 5; c[7][9] = 4; c[7][10] = 3;
c[8][10] = 5; c[8][11] = 6; c[9][12] = 4; c[10][12] = 2;
c[11][12] = 5;
/*以下为获取数据方法
cout << "请输入多段图结点的个数:" << endl;
cin >> n;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
c[i][j] = INF;
}
}
cout << "请输入多段图的段数:" << endl;
cin >> k;
cout << "请输入边的数量:" << endl;
cin >> m;
cout << "请依次输入各边的起点、重点及权值:" << endl;
int x, y, z;
for (int i = 0; i < m; i++)
{
cin >> x >> y >> z;
c[x][y] = z;
}
*/
}