关于动态规划,目前我的理解是:把一个大问题转为小问题,大问题的解依赖于小问题的解得到。
在知乎的这篇回答里有一段总结:
所以一个问题是该用递推、贪心、搜索还是动态规划,完全是由这个问题本身阶段间状态的转移方式决定的!
每个阶段只有一个状态->递推;
每个阶段的最优状态都是由上一个阶段的最优状态得到的->贪心;
每个阶段的最优状态是由之前所有阶段的状态的组合得到的->搜索;
每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到而不管之前这个状态是如何得到的->动态规划。
作者:王勐
链接:https://www.zhihu.com/question/23995189/answer/35429905
来源:知乎
著作权归作者所有,转载请联系作者获得授权。
它里面说的阶段就是我理解的“大问题”“小问题”,而状态就是一些解。下面说一个小题目。
leetCode上这题,这题在算法导论书里提过。
题目
给你一个数组,每个数表示每天的股票价格,如果你只能进行一次交易(一次买入和卖出),怎么做大最大利润?
比如:[7, 1, 5, 3, 6, 4]。最大利润就是6-1。
解答思路
(1)把数组每个数减去前面的一个数,转化成一个差值数组,比如[7, 1, 5, 3, 6, 4]变成[7,-6,4,-2,3,-2]。这样问题就转化成了:求一个数组区间,区间内所有数加起来最大。因为 6 – 1 = (6-3)+(3-5) + (5-1)。
(2)使用“大问题转化成小问题”的思路:
一个n长的数组的解如何跟n-1长数组的解联系起来?如果在这两者之间建立联系,就可以把n长的一直转化成1长的问题,问题就直接解决了。
我的思路是:最优区间分成两种,包含第一个元素和不包含的,不包含的,那么它其实就是n-1长数组的解,即从第2个元素到最后。这样就把n长的大问题转化为n-1长的小问题。
具体代码:
int maxProfit(vector<int>& prices) {
if (prices.empty() || prices.size() == 1) {
return 0;
}
//先转化成差值数组
vector<int> deltPrices;
int lastValue = 0;
for (auto iter = prices.begin(); iter != prices.end(); iter++) {
deltPrices.push_back(*iter - lastValue);
lastValue = *iter;
}
int maxP = maxProfit(deltPrices, 1, prices.size() - 1);
return max(maxP, 0); //如果小于0,则是赔本,还不如不交易
}
int maxProfit(vector<int>& deltPrices, size_t began, size_t end){
if (began == end) {
return deltPrices[began];
}
//求began+1到end的最优解,和包含第一个元素的最优解比较
int profit = maxProfit(deltPrices, began+1, end);
//如果第一数小于0,那么包含第一个的肯定比不包含的要小
if (deltPrices[began] <= 0) {
return profit;
}else{
int leftProfit = leftMaxProfit(deltPrices, began, end);
return max(leftProfit, profit);
}
}
//包含began这第一个数的最优解
int leftMaxProfit(vector<int>& deltPrices, size_t began, size_t end){
int maxProfit = deltPrices[began];
int leftProfit = maxProfit;
for (auto i = began+1; i<end+1; i++) {
leftProfit += deltPrices[i];
if (leftProfit > maxProfit) {
maxProfit = leftProfit;
}
}
return maxProfit;
}
求began+1到end的最优解,和包含第一个元素的最优解比较,哪个打,哪个就是began到end的最优解。