LeetCode中 House Robber问题随笔

先附上题目链接,有两问:

https://leetcode.com/problems/house-robber/

https://leetcode.com/problems/house-robber-ii/

第一问题目如下:

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.

大意就是去偷一排房子,每间房子中的钱数已知,偷的时候不能连续进入相邻的两间房屋,也就是说如果上一家偷了,下一家就不能偷,求出一个最优路径以便偷到最多的钱。

第二问问题就是加了一个限制,即所有房间成环状分布,也就是说首位不能同时偷。

因为没有规定房间数量,也就是说是一个动态的数组问题,可以简化为一个变种树状图:

树状图自生长法:

第一个想法是让计算机重现这个树状图,即先写出1和0,然后1后面的值只能为0,0后面的值分为1和0两个:

我们可以创建一个2维数组,叫做result_map[][],行数代表所有可能的情况,列数代表总共的房间数,若一共有5间房,1 0 1 0 1代表隔间偷这种情况。

那么一共有多少种情况呢, 我们可以从树状图中看到,随着房间数从1到5的变化,总的可能情况为2 3 5 8 13,遵循斐波那契数列,即,对于n的房间数,总的情况为:

是一个提前两项的通项公式,因此根据输入的房间总数,我们可以得到最后这个树状图矩阵的大小:result_map[fab(n)][n].

1.在第一列的第1,2行记录1,0,代表第一间房有偷与不偷两种选择。

2.从第二列,若同一行上一列是1,则这一列为0;若上一列为0,这一列为1。

3.判断出上一列为0的同时,将计数器(这里记为count0)加1,同时在count0+2行开始,记下这列为0的情况。

4.复制当前行前几列的数据。

特别说明的是这个算法是一列一列操作的,对于每一列来说,只需要遍历上一列中所有的可能性即可,对于第2列,遍历的行数是第一列总的可能情况(2种);而对于第4列,要遍历的行数是第3列总的可能情况(5种),这样循环计算可以保证前一列一定是有意义的值,减少初始化的值。

5.计算出整个矩阵后,将矩阵与提前设置的房间里钱数数组相乘,即可得出每种路径的总钱数。

这部分代码如下():

  1 // rob2.cpp : 定义控制台应用程序的入口点。
  2 //
  3
  4 #include "stdafx.h"
  5 #include <vector>
  6 #include <iostream>
  7 #include <iomanip>
  8 #include <math.h>
  9 using namespace std;
 10
 11 int fab(int number)
 12 {
 13     if (number <1)
 14         return 1;
 15     else
 16         return (1 / sqrt(5))*(pow(((1 + sqrt(5)) / 2), number + 2) - pow(((1 - sqrt(5)) / 2), number + 2));
 17 }
 18
 19 int _tmain(int argc, _TCHAR* argv[])
 20 {
 21
 22     int i, j,k, c, r;
 23     int countP=0;
 24     int number;
 25     cout << "enter number of houses: \n";
 26     cin >> number;
 27
 28     vector<int> money_arr(number);
 29     cout << "money for each house: " << endl;
 30     for (int arr = 0; arr < number; arr++)
 31     {
 32         money_arr[arr] = rand() %100;
 33         cout << money_arr[arr] << " ";
 34     }
 35     cout << endl;
 36
 37     int total;
 38     total = fab(number); //计算总的可能性
 39     int row = 0;
 40     row = total;
 41     int column = number + 1; //给计算总钱数留了一列
 42
 43     vector<vector<int> > result_map(row, vector<int>(column));
 44
 45
 46
 47     result_map[0][1] = 1;
 48     result_map[1][1] = 0;
 49
 50
 51
 52
 53     for (c = 2; c < column; c++)
 54     {
 55         for (r = 0; r < fab(c-1); r++)      //第c列的有效
 56         {
 57             if (result_map[r][c - 1] == 1)
 58             {
 59                 result_map[r][c] = 0;
 60             }
 61             else if (result_map[r][c - 1] == 0)
 62             {
 63                 result_map[r][c] = 1;
 64                 countP++;
 65                 result_map[countP+1][c] = 0;
 66                     for (i = c - 1; i > 0; i--)
 67                     {
 68                         result_map[countP+1][i] = result_map[r][i];
 69                     }
 70             }
 71             else{}
 72         }
 73
 74     }
 75
 76
 77
 78     for (c = 1; c < column; c++)
 79     {
 80         for (r = 0; r < row; r++)
 81         {
 82             result_map[r][0] = result_map[r][0] + money_arr[c - 1] * result_map[r][c];
 83         }
 84     }
 85
 86     cout << endl;
 87     cout << "1为抢 " << "0为不抢。" << endl;
 88     cout << endl;
 89     cout << setw(6) << "总钱数" << setw(6) << "第一家" << setw(6) << "第二家" << setw(6) << "第三家";
 90     cout << setw(6) << "第四家" << setw(6) << "第五家" << setw(6) << "第六家" << "..." << endl;
 91
 92     for (j = 0; j < row; j++)
 93     {
 94         for (k = 0; k < column; k++)
 95         {
 96             cout << setw(6) << result_map[j][k] << right;
 97         }
 98         cout << endl;
 99     }
100
101
102
103     return 0;
104 }

运行结果如图:

只需要选出第一列最大值所对应的路径即可。

对于第二问来说,只要排除首尾都为一的矩阵即可。

基于斐波那契数列规律的应用:

上述方法存在一个3次的嵌套循环,对于算法来说应该避免,这个方法中我们可以寻找树状图中的规律,帮助程序画出所有的情况:

这个算法遵循的基本规律是:

1.对于总的房间数n,最终的分叉数为fab(n). —— fab(int n)方法在前面:  fab(0)=1,fab(1)=2,fab(2)=3,fab(3)=5,fab(4)=8,fab(5)=13...

2.在这之中若偷第一间房(第一列为1),会有fab(n-2)种可能性,第一间房不偷(为0),会有fab(n-1)种可能性。

3.每个1后面都是0,而对于第i间房每个0后面,有fab(n-i)个可能性,1后面只有fab(n-i-1)种。

举个例子:在总房间数为5的情况下,第3列的0后面有fab(5-3)=3种可能性,而1后面有fab(5-3-1)=2种可能性。

这个规律怎么用呢:

我们以n=4举例:

Step 1.最终有8种可能性,其中第一间抢,有3种,不抢,有5种,则第一列有3个1,5个0:

Step 2:

进入第二列,一后面都是0:

Step 3:而对于这5个0,其实都是第一步中那一个0的衍生点,根据规律3,这里面有2个1,3个0:

Step 4:3个连续的0依然是上一步一个0的衍生点,这之中有一个1,2个0:

Step 5:最后一列的0是成对出现的,只需要按顺序填一个1,一个0即可:

这个方法代码如下,很杂,有些地方还需要改进,但是结果没有问题,效果一样:

  1 // Rob_project.cpp : 定义控制台应用程序的入口点。
  2 //
  3
  4 #include "stdafx.h"
  5 #include <vector>
  6 #include <iostream>
  7 #include <iomanip>
  8 #include <math.h>
  9 using namespace std;
 10
 11 int fab(int number)
 12 {
 13     if (number <1)
 14         return 1;
 15     else
 16     return (1 / sqrt(5))*(pow(((1 + sqrt(5)) / 2), number + 2) - pow(((1 - sqrt(5)) / 2), number + 2));
 17 }
 18
 19 int _tmain(int argc, _TCHAR* argv[])
 20 {
 21
 22
 23     int n;
 24     cout << "enter number of houses: \n";
 25     cin >> n;
 26
 27     vector<int> money_arr(n);
 28     cout << "money for each house: " << endl;
 29     for (int arr = 0; arr < n; arr++)
 30     {
 31         money_arr[arr] = rand() % 100;
 32         cout << money_arr[arr] << " ";
 33     }
 34     cout << endl;
 35     int total;
 36     total = fab(n); //计算总的可能性
 37     int row = 0;
 38     row = total;
 39     int column = n + 1;
 40
 41     vector<vector<int>> result_map(row, vector<int>(column));
 42
 43     for (int i = 0; i < total; i++)
 44     {
 45         if (i < fab(n - 2))
 46             result_map[i][1] = 1;
 47         else
 48             result_map[i][1] = 0;
 49     }
 50     int k,j = 0;
 51
 52     for (j = 2; j < column - 1; j++)
 53         for (k = 0; k < row;)
 54         {
 55         if (result_map[k][j - 1] == 1)
 56         {
 57             result_map[k][j] = 0;
 58             k++;
 59         }
 60         else if (result_map[k][j - 1] == 0)
 61         {
 62             int number_1 = fab(n - j - 1); //对于每个0后都有fab(n-j+1)的可能性,其中有fab(n - j - 1)个1,fab(n-j)个0
 63             int number_0 = fab(n - j);
 64             for (int i = 0; i < number_1; i++)
 65             {
 66                 result_map[k][j] = 1;
 67                 k++;
 68             }
 69             for (int i = 0; i < number_0; i++)
 70             {
 71                 result_map[k][j] = 0;
 72                 k++;
 73             }
 74         }
 75         }
 76     for (k = 0; k < row; k++)                //最后一列
 77     {
 78         if (result_map[k][column - 2] == 1)
 79         {
 80             result_map[k][column - 1] = 0;
 81         }
 82         else if (result_map[k][column - 2] == 0)
 83         {
 84             result_map[k][column - 1] = 1;
 85             k++;
 86             result_map[k][column - 1] = 0;
 87         }
 88
 89     }
 90
 91
 92
 93     for (int rr = 0; rr < row; rr ++)
 94     {
 95         for (int cc = 1; cc < column; cc ++)
 96         {
 97             result_map[rr][0] = result_map[rr][0] + result_map[rr][cc] * money_arr[cc - 1];
 98         }
 99     }
100
101
102     cout << endl;
103     cout << "1为抢 " << "0为不抢。" << endl;
104     cout << endl;
105     cout << "总抢劫数" << setw(6) << "第一家" << setw(6) << "第二家" << setw(6) << "第三家" << setw(6) << "第四家" << setw(6) << "第五家" << setw(6) << "第六家" << setw(6)<<"..."<<endl;
106     cout << "   ";
107
108     for (int j = 0; j < row; j++)
109     {
110         for (int k = 0; k < column; k++)
111         {
112             cout  << result_map[j][k] <<setw(6)<<right;
113         }
114         cout << endl;
115     }
116
117
118
119     return 0;
120 }

结果跟第一种完全一样:

但是数组的排序方式不同,这一种更整齐一些。

以上。

时间: 2024-10-06 00:40:24

LeetCode中 House Robber问题随笔的相关文章

leetcode中第一题twosum问题解答算法的可行性证明

leetcode中第一题twosum问题解答算法的可行性证明 一.引入 关于leetcode中第一题twosum问题,网上已有不少高人做出过解答,并提出了切实可行的算法实现.我在解答该题时参考了博客http://www.zixue7.com/article-9576-1.html的解答.为让读者更直观地阅读和理解本文,先简要摘录以上博客的内容如下: 题目还原 Two Sum Given an array of integers, find two numbers such that they a

动态规划以及在leetcode中的应用

之前只是知道动态规划是通过组合子问题来解决原问题的,但是如何分析,如何应用一直都是一头雾水.最近在leetcode中发现有好几道题都可以用动态规划方法进行解决,就此做下笔录. 动态规划:应用于子问题重叠情况,原问题的多个子问题间可能含有相同的子子问题,当然,关于将原问题分解成子问题的思路,分治算法也是可行的,但是如果采用分治递归来解决就会出现一些问题.在重复的子问题的计算中,分治算法会忽略到重复问题,也就是说相同的问题,分治算法会计算多次,这样效率会很低.而动态规划算法会仔细安排求解顺序,对每个

leetcode中一些要用到动态规划的题目

需要仔细回顾的题目: 1.Interleaving String   交叉存取字符串 2.Decode Ways   字符串解码 3.Subsets   Subsets II          求一个集合的幂集 leetcode中一些要用到动态规划的题目,布布扣,bubuko.com

leetcode中关于树的dfs算法题

Validate Binary Search Tree Recover Binary Search Tree Symmetric Tree Same Tree Maximum Depth of Binary Tree Construct Binary Tree from Preorder and Inorder Traversal Construct Binary Tree from Inorder and Postorder Traversal Convert Sorted Array to

Leetcode中字符串总结

本文是个人对LeetCode中字符串类型题目的总结,纯属个人感悟,若有不妥的地方,欢迎指出. 一.有关数字 1.数转换 题Interger to roman和Roman to integer这两题是罗马数字和整数之间的相互转换,首先要懂得什么是罗马数字以及相应的组数规则.LeetCode的题中给出的数字最大的是3999,.针对第一题有两种解法:第一是列举出罗马数字在个十百千上的各种情况,形成一个二维矩阵,然后对整数不停的取余.除10来确定相应的罗马数字:第二种是先列出罗马数字组成情况,然后通过从

LeetCode202:Happy Number 。C#版,在vs2010中通过,leetcode中Wrong Answer

static List<int> nums = new List<int>(); public static bool IsHappy(int n) { int newint = 0; while (n != 0) { newint += ((n % 10) * (n % 10)); n = n / 10; } if (newint == 1) return true; if (nums.Contains(newint)) { return false; } else { nums

用Javascript方式实现LeetCode中的算法(更新中)

前一段时间抽空去参加面试,面试官一开始让我做一道题,他看完之后,让我回答一下这个题的时间复杂度并优化一下,当时的我虽然明白什么是时间复杂度,但不知道是怎么计算的,一开局出师不利,然后没然后了,有一次我逛博客园时看到有个博主的文章说到有LeetCode这玩意,于是就知道了LeetCode.忽然有一种疑问:前端学不学算法?我看过一篇博文:为什么我认为数据结构与算法对前端开发很重要? 我觉得,前端应该是要学一下算法的,不久后前端明朗化,要做的工作量不低于后端人员,到时候也会像优化页面一样去优化js,既

leetcode:House Robber(动态规划dp1)

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 autom

有Leetcode中一道题,谈桶排序,基数排序和计数排序

几种非比较排序 在LeetCode中有个题目叫Maximum Gap,是求一个非排序的正数数列中按顺序排列后的最大间隔.这个题用桶排序和基数排序都可以实现.下面说一下桶排序.基数排序和计数排序这三种非比较排序. 桶排序 这种排序的主要思想是,把数列分配到多个桶中,然后再在各个桶中使用排序算法进行排序,当然也可以继续使用桶排序. 假设数组的最大值是A,最小值是B,长度是L,则每个桶的大小可以是S=Max(1,(A-B)/(L-1))则可以分为(A-B)/S+1个桶. 对于数列中的数字x,用(x-B