这是一个从简到繁的步进题目组。
第一个是一维很简单的线性状态转换,题目如下:
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
转移方程如下:
/-- a[1] ,i == 1
|-- max{a[1], a[2]} ,i == 2
rob[i] = | /-- rob[i-2] + a[i]
|-- max | ,i > 2
\ \-- rob[i-1]
思路:对当前第i
个做出决策分为选择和不选择,分别得到两个子问题,比较二者最大即为原问题的最优解。代码如下:
int robI(vector<int>& nums) {
auto n = nums.size();
if (n == 0) return 0;
int * rob = new int[n];
rob[0] = nums[0];
rob[1] = nums[0] > nums[1] ? nums[0] : nums[1];
int res = 0;
for (auto i = 2; i < n; ++i)
{
int tmp = rob[i-2] + nums[i];
rob[i] = rob[i-1] > tmp ? rob[i-1] : tmp;
}
res = rob[n-1];
delete rob;
return res;
}
在第一题的基础上可以围成环,因此情况复杂一些:
/-- a[i], i == j
|
r[i,j] = | /-- r(i+1, j-2) + a[j]
|--max |_/-- r(i, j-1), i != 1
\ | \-- rob(j-1), i == 1
其中需要用到第一题的解rob
,r[i,j]
表示从第i个到第j个元素之间的最优解。
int robII(vector<int>& nums) {
auto n = nums.size();
if (n == 0) return 0;
int * rob = new int[n];
rob[0] = nums[0];
rob[1] = nums[0] > nums[1] ? nums[0] : nums[1];
vector<int> row(n, 0);
vector<vector<int>> r(n, row);
for (int l = 0; l < n; ++l)
{
for (int i = 0; i < n - l; ++i)
{
int j = i + l;
if (j >= 2) rob[j] = rob[j-2] + nums[j] > rob[j-1] ? rob[j-2] + nums[j] : rob[j-1];
if (i == j)
{
r[i][j] = nums[i];
continue;
}
if (i == 0)
{
int a = rob[j-1], b = r[i+1][j-2] + nums[j];
r[i][j] = a > b ? a : b;
}
else
{
int choose = r[i][j-2] + nums[j], noch = r[i][j-1];
r[i][j] = choose > noch ? choose : noch;
}
}
}
delete rob;
return r[0][n-1];
}
题目发展到第三个变种,所有的house成为了一个二叉树,题目如下:
The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the “root.” Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that “all houses in this place forms a binary tree”. It will automatically contact the police if two directly-linked houses were broken into on the same night.
Determine the maximum amount of money the thief can rob tonight without alerting the police.
根据前面两个题目的思路,这里结合二叉树,需要自顶向下来递归求解。为此,需要使用一个记忆变量来记录已经计算过的子问题,否则递归子问题会变成指数式增长。代码实现如下:
int rob(TreeNode* root) {
mapint> rob;
return robhelper(root, rob);
}
int robhelper(TreeNode * p, mapint> & rob)
{
if (!p) return 0;
int no_choose = 0;
if (rob.end() != rob.find(p->left)) no_choose += rob[p->left];
else
{
int l = robhelper(p->left, rob);
rob[p->left] = l;
no_choose += l;
}
if (rob.end() != rob.find(p->right)) no_choose += rob[p->right];
else
{
int r = robhelper(p->right, rob);
rob[p->right] = r;
no_choose += r;
}
int choose = p->val;
if (!p->left && !p->right) return choose > no_choose ? choose : no_choose;
if (p->left)
{
if (rob.find(p->left->left) != rob.end()) choose += rob[p->left->left];
else choose += robhelper(p->left->left, rob);
if (rob.find(p->left->right) != rob.end()) choose += rob[p->left->right];
else choose += robhelper(p->left->right, rob);
if (p->right)
{
if (rob.find(p->right->left) != rob.end()) choose += rob[p->right->left];
else choose += robhelper(p->right->left, rob);
if (rob.find(p->right->right) != rob.end()) choose += rob[p->right->right];
else choose += robhelper(p->right->right, rob);
}
}
else
{
if (rob.find(p->right->left) != rob.end()) choose += rob[p->right->left];
else choose += robhelper(p->right->left, rob);
if (rob.find(p->right->right) != rob.end()) choose += rob[p->right->right];
else choose += robhelper(p->right->right, rob);
}
return choose > no_choose ? choose : no_choose;
}
需要用一个辅助函数进行递归调用,使用一个map
变量来保存已经计算过的子问题,这里的索引使用的是子问题数的根节点指针。
递归调用的代码思路也很明显,分别考察当前节点是否进行选择,不选择就直接计算左右子树子问题并相加,这里计算子问题均是先在rob字典里查找,没找到的时候才递归调用。对于选择当前节点之后,就不能选择其子节点,此时需要分别考虑孙子节点的所有情况并相加,最后返回的是选择或者不选择之中的最大者。
三道题目循序渐进,从一维线性状态到二维动态规划,再到与树形结构与递归的使用结合,是一个非常好的练习案例。三道题目均来自leetcode,已全部提交AC,供需要者使用和交流~_~