1.分别讨论0或1;边界
2.写出递推方程;最优子结构、状态转移方程、重叠子问题
int rob(int* arr, int size){
int dp[size];int result;
if(size==0)return 0;
if(size==1)return arr[0];
else{
dp[0]=arr[0];
dp[1]=arr[1] > arr[0] ? arr[1]:arr[0];
for (int i = 2; i < size; ++i) {
dp[i]= dp[i-1]>arr[i]+dp[i-2] ? dp[i-1]:arr[i]+dp[i-2]; //注意arr 和 dp ;dp进入下一次递推
}
result=dp[size-1];
return result;
低空间写法:
滚轮数组
int rob(int* nums, int numsSize) {
int retVal = 0;
if (numsSize == 0) {
return retVal;
} else if (numsSize == 1) {
return nums[0];
}
int first = nums[0];
int second = fmax(nums[0], nums[1]);
int temp;
int i;
for (i = 2; i < numsSize; ++i) {
temp = second;
second = fmax(first + nums[i], second);
first = temp;
}
retVal = second;
return retVal;
}
遍历修减
int rob(int* nums, int numsSize){
if (numsSize == 1) {
return *nums;
}nums[1] = fmax(nums[0], nums[1]);
for (int i = 2; i < numsSize; i++) {
nums[i] = fmax(nums[i - 2] + nums[i], nums[i - 1]);
}return fmax(nums[numsSize - 1], nums[numsSize - 2]);
}
函数fmax(x,y) 返回较大值;
自底向上的动态规划:
在青蛙跳阶问题中:一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 10 级的台阶总共有多少种跳法。
- f(n-1)和f(n-2) 称为 f(n) 最优子结构
- f(n)= f(n-1)+f(n-2)状态转移方程
- f(1) = 1, f(2) = 2 边界
- 比如f(10)= f(9)+f(8),f(9) = f(8) + f(7) ,f(8) 重叠子问题
for循环从f(1)到f(10)即可;
1. 穷举分析
- 当台阶数是1的时候,有一种跳法,f(1) =1
- 当只有2级台阶时,有两种跳法,第一种是直接跳两级,第二种是先跳一级,然后再跳一级。即f(2) = 2;
- 当台阶是3级时,想跳到第3级台阶,要么是先跳到第2级,然后再跳1级台阶上去,要么是先跳到第 1级,然后一次迈 2 级台阶上去。所以f(3) = f(2) + f(1) =3
2. 确定边界
通过穷举分析,我们发现,当台阶数是1的时候或者2的时候,可以明确知道青蛙跳法。f(1) =1,f(2) = 2
3. 找规律,确定最优子结构,写转移方程和code
一道动态规划问题,其实就是一个递推问题。假设当前决策结果是f(n),则最优子结构就是要让 f(n-k) 最优,最优子结构性质就是能让转移到n的状态是最优的,并且与后面的决策没有关系,即让后面的决策安心地使用前面的局部最优解的一种性质
贪心
找局部最优:
int main() { long long m, s, t, i = 1; long long blink = 0, sum = 0; scanf("%lld %lld %lld", &m, &s, &t); while (i <= t) { sum = sum + 17; if (m >= 10) { blink = blink + 60; m = m - 10; } else m = m + 4; sum = fmax(sum, blink); if (s <= sum) { printf("Yes\n%lld", i); break; } i++; } if (i > t) { printf("No\n"); printf("%d\n", sum); } return 0; }
- 建立数学模型来描述问题
- 把求解的问题分成若干个子问题
- 对每个子问题求解,得到子问题的局部最优解
- 把子问题的解局部最优解合成原来问题的一个解
前提是:局部最优策略能导致产生全局最优解。
贪心算法的实现框架
从问题的某一初始解出发:
while (朝给定总目标前进一步)
{
利用可行的决策,求出可行解的一个解元素。
}
由所有解元素组合成问题的一个可行解;
【背包问题】有一个背包,容量是M=150,有7个物品,物品可以分割成任意大小。要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品:A B C D E F G
重量:35 30 60 50 40 10 25
价值:10 40 30 50 35 40 30
分析:
目标函数: ∑pi最大
约束条件是装入的物品总质量不超过背包容量:∑wi<=M( M=150)
(1)根据贪心的策略,每次挑选价值最大的物品装入背包,得到的结果是否最优?
(2)每次挑选所占重量最小的物品装入是否能得到最优解?
(3)每次选取单位重量价值最大的物品,成为解本题的策略
动态规划的设计,其实就是利用最优子结构和重叠子问题性质对穷举法进行优化,通过将中间结果保存在数组中,实现用空间来换取时间交换,实现程序的快速运行。(动态规划求解时,一般都会转化为网格进行求解,而且因为是空间换时间(避免了子问题的重复计算),因此一般迭代求解)。
动态规划具有两个性质:
1)重叠子问题
2)最优子结构
贪心算法:
1)贪心选择性质
2)最优子结构
解释一下,最优子结构性质是指问题的最优解包含其子问题的最优解时,就称该问题具有最优子结构性质,重叠子问题指的是子问题可能被多次用到,多次计算,动态规划就是为了消除其重叠子问题而设计的。其实贪心算法是一种特殊的动态规划,由于其具有贪心选择性质,保证了子问题只会被计算一次,不会被多次计算,因此贪心算法其实是最简单的动态规划。
考虑最值解,状态小规模问题的数学表示,状态转移方程(大规模问题如何转化为更小的问题),返回值