剑指 Offer II 090+091+092+094+095

环形房屋偷盗

一个专业的小偷,计划偷窃一个环形街道上沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组 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];

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值