环形房屋偷盗
一个专业的小偷,计划偷窃一个环形街道上沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组 nums ,请计算 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
分析:
动态规划的问题。建立数组dp,dp[i]表示最后一个房屋是第i间是所能得到的最高金额,dp[0]=nums[0], dp[1]=max(nums[0], nums[1]), 从i>=2开始可以建立递推公式dp[i] = max(dp[i-2]+nums[i], dp[i-1]).本题还需要讨论第一个房屋偷不偷的问题,进行两次遍历,第一遍时不取nums[0]即从下标一开始遍历dp[0]=nums[1], dp[1]=max(nums[1], nums[2]),此时可以遍历到dp[n-1];第二遍取nums[0],dp[0]=nums[0], dp[1]=max(nums[0], nums[1]),此时只能遍历到dp[n-2],因为最后一个房屋一定不能偷。完成两次循环后,取较大值返回.
class Solution {
public int rob(int[] nums) {
if (nums.length == 1) return nums[0];
if (nums.length == 2) return Math.max(nums[0], nums[1]);
int[] dp1 = new int[nums.length-1];
int[] dp2 = new int[nums.length-1];
dp1[0] = nums[0];
dp1[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < dp1.length; i++) {
dp1[i] = Math.max(dp1[i-1], dp1[i-2]+nums[i]);
}
dp2[0] = nums[1];
dp2[1] = Math.max(nums[1], nums[2]);
for (int i = 2; i < dp2.length; i++) {
dp2[i] = Math.max(dp2[i-1], dp2[i-2]+nums[i+1]);
}
return Math.max(dp1[dp1.length-1], dp2[dp2.length-1]);
}
}
粉刷房子
假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。
当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。
例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。
请计算出粉刷完所有房子最少的花费成本。
分析:
动态规划问题。定义数组dp[i][j], 表示粉刷到第i个房子时,第i个房子为j颜色时所用的最小花费(i<n, j<3)。其中dp[0][0] = costs[0][0], dp[0][1] = costs[0][1], dp[0][2] = costs[0][2]。从i>=1,开始递推dp[i][0] = min(dp[i-1][1],dp[i-1][2])+cost[i][0], dp[i][1] = min(dp[i-1][0],dp[i-1][2])+cost[i][1],dp[i][2] = min(dp[i-1][0],dp[i-1][1])+cost[i][2].最后返回dp[n-1][0]、dp[n-1][1]、dp[n-1][2]中的最小值。
class Solution {
public int minCost(int[][] costs) {
int n = costs.length;
int a = costs[0][0], b = costs[0][1], c = costs[0][2];
for (int i = 1; i < n; i++) {
int d = Math.min(b,c)+costs[i][0];
int e = Math.min(a,c)+costs[i][1];
int f = Math.min(a,b)+costs[i][2];
a = d;
b = e;
c = f;
}
return Math.min(a, Math.min(b, c));
}
}
翻转字串
如果一个由 ‘0’ 和 ‘1’ 组成的字符串,是以一些 ‘0’(可能没有 ‘0’)后面跟着一些 ‘1’(也可能没有 ‘1’)的形式组成的,那么该字符串是单调递增的。
我们给出一个由字符 ‘0’ 和 ‘1’ 组成的字符串 s,我们可以将任何 ‘0’ 翻转为 ‘1’ 或者将 ‘1’ 翻转为 ‘0’。
返回使 s 单调递增 的最小翻转次数。
分析:
定义一个数组dp,dp[i][0] 和 dp[i][1] 分别表示下标 i 处的字符为 0 和 1 的情况下使得 s[0…i] 单调递增的最小翻转次数。递推关系为如果s[i]=1, dp[i][0]=dp[i−1][0];如果s[i]=0,dp[i][1] = min(dp[i-1][0], dp[i-1][1])+1。初始时s[0]=0, dp[0][0]=0; s[0]=1则dp[0][1]=1。最后返回dp[i][1]和dp[i][0]中较小的值。
class Solution {
public static int minFlipsMonoIncr(String s) {
int one = 0;
int dp = 0;
for(int i = 0; i < s.length() ; i++){
char c = s.charAt(i);
if(c == '0'){ //当前等于 0 ,看反转前面全是 1 的为0 和 反转当前为1的代价哪个大
dp=Math.min(one, dp+1);
}else{
++one;//记录前面为1 的个数
}
}
return dp;
}
}
最少回文分割
给定一个字符串 s,请将 s 分割成一些子串,使每个子串都是回文串。
返回符合要求的 最少分割次数 。
分析:
动态规划,dp[i]的含义为字符串长度从0到i,最少可以分为几个回文串。递归关系:当前字符串长度为[0,i]时,如果存在0<j<i && [j,i]是回文串的话,那么dp[i]=min(dp[i],dp[j]+1)。初始dp[0]=0, dp[i]=i,因为长度为i的字符串最多可以分为i个子字符串。最后dp[n]为字符串长度为n时,最少可分为dp[n]个字符串,需要dp[n]-1刀。
class Solution {
public int minCut(String s) {
if (s.length() == 0) return 0;
int n = s.length();
int[] dp = new int[n+1];
dp[0] = 0;
for (int i = 1; i <=n; i++) {
dp[i] = i;
for (int j = 0; j < i; j++) {
if (isHui(s, j, i-1)){
dp[i] = Math.min(dp[j]+1, dp[i]);
}
}
}
return dp[n]-1;
}
public boolean isHui(String s, int l, int r){
while (l<r){
if (s.charAt(l) != s.charAt(r)) return false;
l++;
r--;
}
return true;
}
}
最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
- 例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
分析:
二维动态规划问题。dp[i][j] 表示 text1[0:i]和text2[0:j]的最长公共子序列长度。初始化时dp[0][j]与dp[i][0]都设为0,当 i>0 且 j>0 时,递归方程为:当text1[i-1] == text2[j-1]时,dp[i][j]=dp[i-1][j-1]+1;否则dp[i][j] = max(dp[i-1][j], dp[i][j-1])。
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length();
int n = text2.length();
int[][] dp = new int[m+1][n+1];
for (int i = 1; i < m+1; i++) {
char c = text1.charAt(i-1);
for (int j = 1; j < n+1; j++) {
if (c == text2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1]+1;
}else {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[m][n];
}
}