动态规划

作者: Phoebe_Liu | 来源:发表于2018-12-14 10:30 被阅读14次
    1. 问题:原字符串拆分成回文串的最小切割数
      设:f(i)是从i开始到结尾的最小切割数(从后->前遍历)
      则:f(i)=min{f(j+1)+1},i≤j<n (其中,s[i] =s[j], i和j之间构成了一个小回文序列)
    public int minCut(String s) {
            if(s == null || s.length() <= 1)
                return 0;
            
            int len = s.length();
            
            boolean[][] p = new boolean[len][len];
            int[] dp = new int[len+1];
            
            for(int i = 0 ; i < len; i++){
                for(int j =0; j<len;j++){
                    p[i][j] = false;
                }
            }
            
            for(int i = len; i >=0; i--){
                dp[i] = len-i-1;
            }
            
            for(int i = len-1; i >=0 ;i--){
                for(int j=i; j <len;j++){
                    if(s.charAt(i) == s.charAt(j)){
                        if(j-i<=1 || p[i+1][j-1] == true){
                            p[i][j] = true;
                            dp[i] = Math.min(dp[i] , dp[j+1] +1 );
                        }
                    }
                }
            }
            return dp[0];
        }
    
    1. 乘积最大子序列:给定一个数组,求其中连续乘积值最大的结果
      纠结点:之前的最大值,可能因为×负数,就变成最小;之前的最小值,可能因为×负数,变最大。
      设:f[i]表示子数组[0, i]范围内的最大子数组乘积,g[i]表示子数组[0, i]范围内的最小子数组乘积
      则:f[i] = f[i-1] * nums[i],g[i-1] * nums[i],和nums[i] 中的最大值;
      g[i] = f[i-1] * nums[i],g[i-1] * nums[i],和nums[i] 中的最小值;
    public class Solution {
        /**
         * @param nums: an array of integers
         * @return: an integer
         */
        public int maxProduct(int[] nums) {
            // write your code here
            if (nums == null || nums.length == 0) {
                return 0;
            }
            int minPre = nums[0], maxPre = nums[0];
            int max = nums[0], min = nums[0];
            int res = nums[0];
            for (int i = 1; i < nums.length; i ++) {
                max = Math.max(nums[i], Math.max(maxPre * nums[i], minPre * nums[i]));
                min = Math.min(nums[i], Math.min(maxPre * nums[i], minPre * nums[i]));
                res = Math.max(res, max);
                maxPre = max;
                minPre = min;
            }
            return res;
        }
    }
    
    1. 整数拆分:将一个数字拆分成多个,求拆分后的最大乘积
      设:f[n] 表示数字为 n 时的最优值,
      则: f[n] = max(i, f[i]) * max(j * f[j]) 其中 i + j == n
     public int integerBreak(int n) {
            int[] dp = new int[n+1];
            
            dp[0] = 0;
            dp[1] = 1;
            
            for(int i = 2; i<=n;i++){
                for(int j =1; j <i; j++){
                    dp[i] = Math.max(dp[i],Math.max(j, dp[j]) * Math.max((i-j) , dp[i-j])) ;
                }
                
            }
            
            return dp[n];
        }
    
    1. 递增子序列的最长长度
      设: f[i]是以i为结尾的 最长递增子序列的长度
      则: f[i] = max{f[i] , f[j] +1 } (其中 j 需要满足: nums[j]< nums[i])
    public int lengthOfLIS(int[] nums) {
            if(nums == null || nums.length == 0)
                return 0;
            
            if(nums.length == 1)
                return 1;
            
            
            // 声明:dp数组
            int[] dp = new int[nums.length];
            for(int i =0; i < dp.length; i++){
                dp[i] = 1;
            }
            
            // 递归计算
            int max_len=0;
            for(int i=1; i< nums.length; i++){
                
                // 需要找到 在i前面,并且比i小的j
                for(int j = 0; j< i; j++){
                    if(nums[j] < nums[i])
                    {
                        dp[i] = Math.max(dp[i] , dp[j] + 1);
                    }
                }
                
                if(dp[i] > max_len)
                    max_len = dp[i];
            }
            
            return max_len;
            
            
        }
    
    1. 找出:递增子序列最长的 子序列个数
      设:len[i]为[0,i]之间,递增子序列的最长长度; cnt[i]为当len[i]当前最长时,可达到这一长度的序列个数
      则:
      从前->后遍历i 从前->i遍历j:
      若:nums[j] < nums[i]: 若len[i] < len[j] +1,则len[i] = len[j] +1且 cnt[i] = cnt[j];
      若len[i] = len[j] +1,则cnt[i] += cnt[j]
    int findNumberOfLIS(vector<int>& nums) {
            int res = 0, mx = 0, n = nums.size();
            vector<int> len(n, 1), cnt(n, 1);
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < i; ++j) {
                    if (nums[i] <= nums[j]) continue;
                    if (len[i] == len[j] + 1) cnt[i] += cnt[j];
                    else if (len[i] < len[j] + 1) {
                        len[i] = len[j] + 1;
                        cnt[i] = cnt[j];
                    }
                }
                if (mx == len[i]) res += cnt[i];
                else if (mx < len[i]) {
                    mx = len[i];
                    res = cnt[i];
                }
            }
            return res;
        }
    
    1. 矩阵中的最长递增路径
      设:dp[i][j]是以(i,j)开始的最长路径
      则:dp[i][j] = max{dp[i+-1][j+-1]} +1([i+-1][j+-1]代表了(i,j)邻居节点里 数值大于matrix(i,j)本身的邻居 的最大路径)
    int[][] direction = {{0,-1},{0,1},{1,0},{-1,0}};
        
        public int longestIncreasingPath(int[][] matrix) {
            if(matrix == null || matrix.length == 0 || matrix[0].length == 0){
                return 0;
            }
            
            int m = matrix.length;
            int n = matrix[0].length;
            
            int[][] dp = new int [m][n]; // 记录:以该点为起点时,最长路径的长度
            int max = 1;
            
            
            for(int i =0 ; i < m; i++){
                for(int j =0 ; j < n; j++){
                        int res = dfs(matrix, dp, i, j);
                        max = Math.max(max, res);
                }
            }
            
            return max;
        }
        
        public int dfs(int[][] matrix,int[][] dp, int cur_x, int cur_y){
            if(dp[cur_x][cur_y] !=0)
                return dp[cur_x][cur_y];
            
            int max = 1;
            for(int i =0 ;i < 4; i ++){
                int add_x = direction[i][0];
                int add_y = direction[i][1];
                
                int x = add_x + cur_x;
                int y = add_y + cur_y;
                
                if(x >=0 && x< matrix.length && y >=0 && y< matrix[0].length  && matrix[x][y] > matrix[cur_x][cur_y] ){
                     dp[x][y] = dfs(matrix, dp, x, y);
                     max = Math.max(dp[x][y] +1 , max);
                }
                
            }
            return max;
            
        }
    
    1. 最长匹配括号
      设:dp[i]代表了从index=0开始,匹配到index=i, 最长匹配括号的长度
      则:if(s[i] == '(') dp[i]=0
      if(s[i]==')')
      if(s[i-1]=='(') 则dp[i] = 2+dp[i-2]
      else if(i-dp[i-1]-1>0 && s[i-dp[i-1]-1]=='(') // 把紧挨着、之前匹配的都掠过去,看上一次未匹配的字母是否为左括号
      则dp[i]=dp[i-1]+2 + dp[i-dp[i-1]-2]
    
     public int longestValidParentheses(String s) {
            int[] dp=new int[s.length()];
            int max=0;
            for (int i=1;i<s.length();i++){
                if (s.charAt(i)=='(') dp[i]=0;
                else{
                    if (s.charAt(i-1)=='(') dp[i]=2+(i==1?0:dp[i-2]);
                    else {
                        if ((i-dp[i-1]-1>-1)&&s.charAt(i-dp[i-1]-1)=='(')
                            dp[i]=dp[i-1]+(i-dp[i-1]-1==0?0:dp[i-dp[i-1]-2])+2;
                    }
                }
                max=Math.max(max,dp[i]);
            }
            return max;
    }
    
    1. Profitable schema——计算方法的个数(注意不是计算最大收益,而是能达到这种收益的配置方案有几个?)
      Input: G = 5, P = 3, group = [2,2], profit = [2,3]
      Output: 2
      Explanation:
      To make a profit of at least 3, the gang could either commit crimes 0 and 1, or just crime 1.
      In total, there are 2 schemes.
    public int profitableSchemes(int G, int P, int[] group, int[] profit) {
            int task = group.length;
            int MOD = 1000000007;
            int[][] dp = new int[G+1][P+1]; // 选取 j 个人员,获得 k 的收益 的方法数。
            dp[0][0] = 1;
            
            // 正序遍历项目(只有 被隐匿掉的变量 是正序的)
            for(int i=1;i<=task; i++){
                int g=group[i-1]; // 该项目用人
                int p=profit[i-1]; // 该项目利润
                
                // 倒序遍历人
                for(int j=G;j>=g;j--){
                    for(int k=P;k>=0;k--){
                        dp[j][k] = dp[j][k] +  // 不用k项目时的schema个数
                                (j>=g? dp[j-g][Math.max(0, k-p)]:0); // 利用k项目的schema个数
                        
                        dp[j][k] = dp[j][k]%MOD;
                    }
                    
                }
            }
            
            long res = 0L;
            for(int i=0;i<=G;i++){
                res = (res+dp[i][P])%MOD;
            }
            
            return (int)res;
        }
    
    1. Combination Sum IV

    Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.

    Example:

    nums = [1, 2, 3]
    target = 4

    The possible combination ways are:
    (1, 1, 1, 1)
    (1, 1, 2)
    (1, 2, 1)
    (1, 3)
    (2, 1, 1)
    (2, 2)
    (3, 1)

    Note that different sequences are counted as different combinations.

    Therefore the output is 7.
    设:dp[i] 代表数字之和=i时的组合方法数
    则:dp[i]= nums[0] 与 dp[i-nums[0]]的结合数

    • nums[1] 与 dp[i-nums[1]]的结合数
    • nums[2] 与 dp[i-nums[2]]的结合数
      =dp[i-nums[0]] + dp[i-nums[1]] + ……
    public int combinationSum4(int[] nums, int target) {
            int[] dp = new int[target+1];
            dp[0] = 1;
            
            // 外层遍历:sum总和
            for(int sum=1;sum<=target;sum++){
                // 内层遍历:各个num数字
                for(int num: nums){
                    if(num<=sum){
                        dp[sum] += dp[sum-num];
                    }
                }
                
            }
            
            return dp[target];
        }
    
    1. Distinct Subsequences II
      思路:简历一个26位的数组endCharCount,endCharCount[0]记录在字符串S中 以'a'为结尾的子串个数
      endCharCount[0] = endCharCount[*] + 1
      解释:+1,代表了加上'a'本身
     public int distinctSubseqII(String S) {
            int[] endCharCount = new int[26];   // 以各个字符结尾的子串个数
            int MOD = 1000000007;
            
            // 外层遍历: 字符串的各个字符
            for(char ch: S.toCharArray()){
                
                // 内层遍历:已经找到的  (以各个字符结尾的子串个数)
                int cur_sum = 0;
                for(int tmpCount: endCharCount){
                    cur_sum = (cur_sum+ tmpCount)%MOD;
                }
                endCharCount[ch-'a'] = cur_sum + 1;
            }
            
            int res=0;
            for(int tmpCount: endCharCount){
                res = (res+tmpCount)%MOD;
            }
            
            return res;
        }
    

    相关文章

      网友评论

        本文标题:动态规划

        本文链接:https://www.haomeiwen.com/subject/qfpwhqtx.html