一、基本思想
- 将待求问题分解为若干个子问题,先求子问题,然后将这些子问题的解得到为原问题的解。
- 用分治法解决同类问题时,相同的子问题会被求解多次。动态规划法用一张表来记录所有已解决问题的子问题的答案。
二、 步骤
- (划分)多阶段决策过程,每步处理一个子问题,界定子问题的边界(初值问题)。
- 列出优化函数的递推方程及初值(无比关键)。
- 问题要满足优化原则或者最优子结构性质(一个最优决策序列的任何子序列本身一定是相对于子序列的初始和结束状态的最优决策序列)。
三、典型实例
- 0-1背包问题:
有一个背包,他的容量为C(Capacity)。现在有n种不同的物品编号分别为0、1…n-1。其中每一件物品的重量为w(i),价值为v(i)。问可以向这个背包中放入哪些物品,使得在不超过背包容量的基础上,背包内物品价值最大。
public int maxValue(int[] weights, int[] values, int w)
{
int n = weights.length;
int[][] vw = new int[n][w + 1];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < w + 1; j++)
{
vw[i][j] = -1;
}
}
vw[0][0] = 0;
vw[0][weights[0]] = values[0];
for (int i = 1; i < n; i++)
{
for (int j = 0; j <= w; j++)
{
if (vw[i - 1][j] >= 0)
{
vw[i][j] = vw[i - 1][j];
}
}
for (int j = w - weights[i]; j >= 0; j--)
{
if (vw[i - 1][j] >= 0)
{
int v = vw[i - 1][j] + values[i];
if (v > vw[i][j + weights[i]])
{
vw[i][j + weights[i]] = v;
}
}
}
}
int maxValue = 0;
for (int i = 0; i <= w; i++)
{
if (vw[n - 1][i] > maxValue)
{
maxValue = vw[n - 1][i];
}
}
return maxValue;
}
public static void main(String[] args)
{
int[] weights = {3, 4, 7, 8, 9};
int[] values = {4, 5, 10, 11, 13};
final Packet packet = new Packet();
System.out.println(packet.maxValue(weights, values, 17));
}
- 最大子段和问题:求一个序列的最大子段和即最大连续子序列之和。
例如序列[4, -3, 5, -2, -1, 2, 6, -2]的最大子段和为11=[4+(-3)+5+(-2)+(-1)+(2)+(6)]。
原问题:考虑最大子段和原问题:给定nn个数(可以为负数)的序列(a1,a2,…,an)(a1,a2,…,an),求max{0,max1≤i≤j≤n∑jk=iak}max{0,max1≤i≤j≤n∑k=ijak}
子问题界定:设前边界为1,后边界为ii,且C(i)C(i)是子序列A[1,…i]A[1,…i]必须包含元素A[i]A[i]的向前连续延伸的最大子段和:
C[i]=max1≤k≤i{∑j=kiA[j]}
C[i]=max1≤k≤i{∑j=kiA[j]}
递推方程满足:
C[i]=max{C[i−1]+A[i], A[i]}i=2,…,nC[1]={A[1] ifA[1]>00 ifA[1]<0
C[i]=max{C[i−1]+A[i], A[i]}i=2,…,nC[1]={A[1] ifA[1]>00 ifA[1]<0
遍历所有以i (1≤i≤n)i (1≤i≤n)为后边界的最大子段和CiCi得出最优解:
OPT(A)=max1≤i≤n{Ci}
int MaxSubsequenceSum(const int A[], int n)
{
int tempSum = A[0];
int maxSum = 0;
for (int j = 0;j < n;j++) // 子问题后边界
{
tempSum = (tempSum + A[j]) > A[j] ? (tempSum + A[j]) : A[j];
if (tempSum > maxSum) // 更新最大和
maxSum = tempSum;
}
return maxSum;
}