斐波那契问题
爬楼梯
leetcode 70 爬楼梯(简单)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.1 阶 + 1 阶
2.2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
题解:
class Solution {
public int climbStairs(int n) {
//爬第k阶台阶,可以是从第k-1阶爬上来,也可以是从第k-2阶爬上来,所以,爬上第k阶楼梯的方法为爬上第k-1阶楼梯的方法+爬上第k-2阶楼梯的方法
//动态规划实现
if(n==1){
return 1;
}
int[] dp = new int[n+1]; //dp[k]为爬上第k阶楼梯的方法数
dp[1] = 1;
dp[2] = 2;
for(int k=3; k<=n; k++){
dp[k] = dp[k-2]+dp[k-1];
}
return dp[n];
}
}
打家劫舍
leetcode 198 打家劫舍(简单)
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
题解:
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n == 0){
return 0;
}
//定义状态,dp[k] 为考虑盗取第[0,k]号房子所取得的最大收益
int[] dp = new int[n];
dp[0] = nums[0];
//状态转移方程,求解dp[k], dp[k] = max( nums[k]+dp[k-2], nums[k-1]+dp[k-3]... )
//或者,状态转移方程,求解dp[k], dp[k] = max( nums[k]+dp[k-2], nums[k-1]+dp[k-3])
for(int k=1; k<n; k++){
dp[k] = 0;
for(int i = k; i>=0; i--){
dp[k] = Math.max((nums[i]+(i>=2 ? dp[i-2]:0)), dp[k]);
}
}
return dp[n-1];
}
}
题解2:
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n==0){
return 0;
}
int[] dp = new int[n];
dp[0] = nums[0];
for(int k=1; k<n; k++){
dp[k] = Math.max(dp[k-1], nums[k]+(k-2>=0? dp[k-2]:0));
}
return dp[n-1];
}
}
环形区域内打家劫舍
leetcode 213 打家劫舍II(中等)
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
题解:
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n==0){
return 0;
}
if(n == 1){
return nums[0];
}
// 为了避免头尾同时被选到的情况,分别对去头和去尾的nums作打家劫舍,选择最优结果
int res1 = subRob(nums, 0, n-2);
int res2 = subRob(nums, 1, n-1);
return res1>res2? res1:res2;
}
// 对nums的[l,r]区间进行打家劫舍,l<=r
private int subRob(int[] nums, int l, int r){
int[] dp = new int[r-l+1];
dp[0] = nums[l];
for(int k=l+1; k<=r; k++){
dp[k-l] = Math.max(dp[k-l-1], nums[k]+(k-l-2>=0? dp[k-l-2]:0));
}
return dp[r-l];
}
}
信件错排
题目描述: 有 N 个 信 和 信封,它们被打乱,求错误装信方式的数量。
题解: 定义一个数组 dp 存储错误方式数量,dp[i] 表示前 i 个信和信封的错误方式数量。假设第 i 个信装到第 j 个信封里面,而第 j 个信装到第 k 个信封里面。根据 i 和 k 是否相等,有两种情况:
- i == k,交换 i 和 k 的信后,它们的信和信封在正确的位置,但是其余 i-2 封信有 dp[i-2] 种错误装信的方式。由于 j 有 i-1 种取值,因此共有 (i-1)*dp[i-2] 种错误装信方式。
- i != k,交换 i 和 j 的信后,第 i 个信和信封在正确的位置,其余 i-1 封信有 dp[i-1] 种错误装信方式。由于 j 有 i-1 种取值,因此共有 (i-1)*dp[i-1] 种错误装信方式。
综上所述,错误装信数量方式数量为:
d p [ i ] = ( i − 1 ) ∗ d p [ i − 1 ] + ( i − 1 ) ∗ d p [ i − 2 ] dp[i] = (i-1)*dp[i-1]+(i-1)*dp[i-2] dp[i]=(i−1)∗dp[i