左神数据结构与算法(中级提升)——04

题目三十一:二叉树的递归套路(树形dp)

问题1:二叉树转双向链表

双向链表节点结构和二叉树结点结构是一样的,如果你把last认为是left,next认为是next的话。

给定一个搜索二叉树的头节点head,请转换成一条有序的双向链表,并返回链表的头节点。

package class06;

import java.util.List;

/**
 * 双向链表节点结构和二叉树结点结构是一样的,如果你把last认为是left,next认为是next的话。
 * 给定一个搜索二叉树的头节点head,请转换成一条有序的双向链表,并返回链表的头节点。
 */
public class Code02_BSTChangeToListNode {

    //搜索二叉树结点
    class Node{
        int value;
        Node left;
        Node right;

        public Node(int value){
            this.value = value;
        }
    }

    //搜索二叉树结点转换成双向链表结点
    //使用递归套路  左子树要头和尾 右子树要头和尾
    public Node BSTNodeChangeToListNode(Node head){
        return process(head).start;
    }

    class Info{
        Node start;
        Node end;

        public Info(Node start,Node end){
            this.start = start;
            this.end = end;
        }
    }

    public Info process(Node X){
        if(X == null){
            return new Info(null,null);
        }
        Info leftInfo = process(X.left);
        Info rightInfo = process(X.right);
        if(leftInfo.end != null){
            leftInfo.end.right = X;
        }
        X.left = leftInfo.end;
        X.right = rightInfo.start;
        if (rightInfo.start != null){
            rightInfo.start.left = X;
        }
        return new Info(leftInfo.start != null ? leftInfo.start : X,
                rightInfo.end != null ? rightInfo.end : X);
    }
}

问题2:最大二叉搜索子树

找到一颗二叉树中,最大搜索二叉子树,返回最大搜索二叉子树的个数

分析:当前结点X

X无关:左子树(maxBSTHead BSTsize

                右子树(maxBSTHead  BSTsize

X有关:

       左子树是不是搜索二叉树,右子树是不是搜索二叉树,左子树的最大值,右子树的最小值,二叉树的大小

因此,递归套路返回信息中包含:最大搜索二叉树的头节点,是不是搜索二叉树,最大值,最小值,size

package class06;

/**
 * 找到一颗二叉树中,最大搜索二叉子树,返回最大搜索二叉子树的个数
 */
public class Code03_MaxSonBST {

    class Node{
        int value;
        Node left;
        Node right;

        public Node(int value){
            this.value = value;
        }
    }

    class Info{
        Node maxBSTHead;
        boolean isBST;
        int max;
        int min;
        int maxBSTSize;

        public Info(Node maxBSTHead,boolean isBST,int max,int min,int maxBSTSize){
            this.maxBSTHead = maxBSTHead;
            this.isBST = isBST;
            this.max = max;
            this.min = min;
            this.maxBSTSize = maxBSTSize;
        }
    }

    public Info process(Node x){
        if (x == null){
            return null;
        }
        Info leftInfo = process(x.left);
        Info rightInfo = process(x.right);
        int max = x.value;
        int min = x.value;
        if(leftInfo != null){
            min = Math.min(min,leftInfo.min);
            max = Math.max(max,leftInfo.max);
        }
        if(rightInfo != null){
            min = Math.min(min,rightInfo.min);
            max = Math.max(max,rightInfo.max);
        }
        Node maxBSTHead = null;
        int maxBSTSize = 0;
        if(leftInfo != null){  //左子树是二叉搜索树,更新可能性1
            maxBSTSize = leftInfo.maxBSTSize;
            maxBSTHead = leftInfo.maxBSTHead;
        }
        if(rightInfo != null && rightInfo.maxBSTSize > maxBSTSize){ //右子树是二叉搜索树,并且比左边的好
            maxBSTSize = rightInfo.maxBSTSize;
            maxBSTHead = rightInfo.maxBSTHead;
        }
        boolean isBST = false;
        //左子树和有子树都是二叉搜索树,并且左子树的最大值<x.value,右子树的最小值>x.value
        if((leftInfo == null || leftInfo.isBST) && (rightInfo == null || rightInfo.isBST)){
            if((leftInfo == null || leftInfo.max < x.value) && (rightInfo == null || rightInfo.min > x.value)){
                isBST = true;
                maxBSTHead = x;
                int leftSize = leftInfo == null ? 0 : leftInfo.maxBSTSize;
                int rightSize = rightInfo == null ? 0 : rightInfo.maxBSTSize;
                maxBSTSize = leftSize + rightSize + 1;
            }
        }
        return new Info(maxBSTHead,isBST,max,min,maxBSTSize);
    }

    public int getSonBST(Node head){
        if (head == null){
            return 0;
        }
        return process(head).maxBSTSize;
    }

}

题目三十二:子数组的最大累加和

为了保证招聘信息的质量问题,公司为每个职位设计了打分系统,打分可以为正数,也可以为负数,正数表示用户认可帖子质量,负数表示用户不认可帖子质量。打分的分数根据评价用户的等级大小不定,比如可以打1分,10分,30分,-10分等。假设数组A记录了一条帖子所有的打分记录,现在需要找出帖子曾经得到过最高的分数是多少,用于后续根据最高分数来确认需要对发帖用户做相应的惩罚或奖励。其中,最高分的定义为:用户所有打分记录中,连续打分数据之和的最大值即认为是帖子曾经获得的最高分。例如:帖子10001010近期打分记录为:[1,1,-1,-10,11,4,-6,9,20,-2],那么该条帖子曾经达到过的最高分数为11+4+(-6)+9+20=38。请实现一段代码,输入为帖子近期的打分记录,输出为当前帖子得到的最高分数。

分析:两个变量,一个cur=cur+arr[i],一个max跟踪cur,但只保证最大,当cur<0,cur=0;else cur不变

package class06;

import org.junit.Test;

/**
 * 最高分定义:用户所有打分记录中,连续打分数据之和的最大值即认为是帖子曾经获得的最高分。
 * 。例如:帖子10001010近期打分记录为:
 * [1,1,-1,-10,11,4,-6,9,20,-2],那么该条帖子曾经达到过的最高分数为11+4+(-6)+9+20=38。
 */
public class Code04_GradeSystem {
    
    public int maxGrade1(int[] arr){
        if (arr == null || arr.length == 0){
            return 0;
        }
        int cur = 0;
        int max = Integer.MIN_VALUE;
        for(int i = 0;i < arr.length;i++){
            cur += arr[i];
            max = Math.max(max,cur);
            cur = cur < 0 ? 0 : cur;   //cur小于0说明之前累加和负数
        }
        return max;
    }

    //暴力解法,时间复杂度为O(N^2)
    public int maxGrade2(int[] arr){
        if(arr == null || arr.length == 0){
            return 0;
        }
        int[] sum_01 = new int[arr.length];  //记录第一位到当前位置的总和
        sum_01[0] = arr[0];
        for(int i = 1;i < sum_01.length;i++){
            sum_01[i] = sum_01[i-1] + arr[i];
        }
        int maxGrade = sum_01[0];
        for (int i = 1;i < sum_01.length;i++){  //这个里面会丢失与sum_01[0]的比较
            for (int j = i;j < sum_01.length;j++){
                int cur = sum_01[j] - sum_01[i - 1];
                maxGrade = Math.max(maxGrade,cur);
            }
        }
        return maxGrade;
    }

    @Test
    public void test(){
        int[] arr = {1,-1,-1,-10,11,-4,-6,-9,-20,-2};
        System.out.println(maxGrade2(arr));
    }
}

给定一个整型矩阵,返回子矩阵的最大累加和

分析:逐层分析,0~0,0~1,0~2,0~3...1~1,1~2...2~2,2~3...把每一层对应列加在一起,就变成了一个一维的数组,变成求一维数组的最大累加和

package class06;

/**
 * 给定一个整型矩阵,返回子矩阵的最大累加和
 */
public class Code05_MaxSum {
    
    public int maxSum(int[][] arr){
        if (arr == null || arr.length == 0 || arr[0].length == 0){
            return 0;
        }
        int max = Integer.MIN_VALUE;
        int cur = 0;
        int[] s = null;  //定义一个一维数组,保存每一列累加起来的值
        for(int i = 0;i < arr.length;i++){  //开始的行号 i
            s = new int[arr[0].length];
            for (int j = i;j < arr.length;j++){  //结束的行号 j  i~j
                cur = 0;
                for (int k = 0;k < s.length;k++){ //每次的 i~j列 的累加值放在 s 数组中
                    s[k] += arr[j][k];
                    cur += s[k];
                    max = Math.max(max,cur);
                    cur = cur < 0 ? 0 : cur;
                }
            }
        }
        return max;
    }
}

题目三十三:贪心

小Q正在给一条长度为n的道路设计安置方案。

为了让问题更简单,小Q把道路视为n个方格,需要照亮的地方用’.’表示,不需要照亮的障碍物格子用’X’表示。小Q现在要在道路上安置一些路灯,对于安置在pos位置的路灯,这栈路灯可以照亮pos-1,pos,pos+1这三个位置。小Q希望能安置尽量少的路灯就照亮所有’.’区域。希望你能帮他计算一下最少需要多少盏路灯。

输入描述:

输入的第一行包含一个正整数t(1<=t<=1000),表示测试用例数

接下来的每两行一个测试数据,第一行一个正整数n(1<=n<=1000),表示道路的长度。第二行一个字符串s表示道路的构造,只包含’.’和’X’。

输出描述:

对于每个测试用例,输出一个正整数表示至少需要多少盏路灯。

package class06;

public class Code06_Light {

    //s除了'.'就是'X'
    //路灯可以影响左中右三个位置
    //贪心
    public int minLight(String s){
        if(s == null || s.length() == 0){
            return 0;
        }
        char[] chars = s.toCharArray();
        int index = 0;
        int light = 0;
        while (index < chars.length){
            if(chars[index] == 'X'){
                index++;
            }else { //当前位置为'.'
                light++;
                if(index + 1 == chars.length){  //最后一个位置跳出
                    break;
                }else {
                    if (chars[index + 1] == 'X'){ // i+1位置是障碍物,跳到i+2位置
                        index = index + 2;
                    }else {  //i+1位置是'.',灯直接放在i+1位置,可以照亮i,i+1,i+2,因此之直接跳到i+3位置
                        index = index + 3;
                    }
                }
            }
        }
        return light;
    }
}

题目三十四:根据前序和中序遍历的数组得到后序遍历的数组

已知一颗二叉树中没有重复节点,并且给定了这棵树的中序遍历数组和先序遍历数组,返回后序遍历数组。

eg:int[] pre = {1,2,4,5,3,6,7}

    int[] in = {4,2,5,1,6,3,7}

返回
    {4,5,2,6,7,3,1}

分析:先序遍历的第一个一定是后序遍历的最后一个

pre 中左右  in 左中右

因此先找到先序遍历pre数组的 这个节点,再找到其在中序遍历in数组的 这个节点,那么在中序遍历 这个节点之前的是左数,节点之后是右树,可以确定在先序遍历左树和右树的范围,根据两个共同的左树和右树,再寻找 这个节点,依次递归。

package class06;

/**
 *已知一颗二叉树中没有重复节点,并且给定了这棵树的中序遍历数组和先序遍历数组,返回后序遍历数组。
 */
public class Code07_PosFromPreAndIn {

    public int[] preAndInGetPos(int[] pre,int[] in){
        if(pre == null || in == null || pre.length != in.length) {
            return null;
        }
        int N = pre.length;
        int[] pos = new int[N];
        set(pre,in,pos,0,N-1,0,N-1,0,N-1);
        return pos;
    }

    public void set(int[] pre,int[] in,int[] pos,
                    int prei,int prej,
                    int ini,int inj,
                    int posi,int posj){
        if(prei > prej){
            return;
        }
        if(prei == prej){ //只剩下最后一个元素
            pos[posi] = pre[prei];
            return;
        }
        pos[posj] = pre[prei];  //先序遍历的一个数的第一个 一定是 后序遍历的最后一个
        int find = ini;
        for (;find <= inj;find++){
            if(in[find] == pre[prei]){ //找到中序遍历的 中 这个节点所在的位置,确定左子树和右子树
                break;
            }
        }
        // ini...inj整棵树的范围
        // find-ini 确定左子树的个数
        set(pre,in,pos,prei+1,prei+(find-ini),ini,find-1,posi,posi+(find-ini)-1);//左子树
        set(pre,in,pos,prei+(find-ini)+1,prej,find+1,inj,posi+(find-ini),posj-1);//右子树
    }
}

题目三十五:输出中文表达下的数字

把一个数字用中文表示出来。数字范围为[0~99999]。

为了方便输出,使用字母替换相应的中文,万W 千Q 百B 十S 零L。使用数字取代中文数字注:对于11应该表示为一十一(1S1),而不是十一(S1)

输入描述:

数字0(包含)到9999(包含)。

输出描述:

eg:12001  输出:1W2QL1

package class07;

import org.junit.Test;

/**
 * 用中文表述数字
 * 12001  输出:1W2QL1
 * 11 1S1
 */
public class Code01_ChineseRepresentationNumber {

    //只接收范围1~9
    public String num1To9(int num){
        if(num < 1 || num > 9){
            return "";
        }
        String[] names = {"一","二","三","四","五","六","七","八","九"};
        return names[num - 1];
    }

    //只接受范围1~99
    public String num1To99(int num,boolean hasBai){
        if (num < 1 || num > 99){
            return "";
        }
        if (num < 10){
            return num1To9(num);
        }
        //有十位
        int shi = num / 10;
        if(shi == 1 && (!hasBai)){
            return "十" + num1To9(num % 10);
        }else {
            return num1To9(shi) + "十" + num1To9(num % 10);
        }
    }

    public  String num1To999(int num){
        if (num < 1 || num > 999){
            return "";
        }
        if(num < 100){
            return num1To99(num,false);
        }
        String res = num1To9(num / 100) + "百";
        int rest = num % 100;
        if (rest == 0){
            return res;
        }else if(rest >= 10) {
            res += num1To99(rest,true);
        }else {
            res += "零" + num1To9(rest);
        }
        return res;
    }

    public String num1To9999(int num){
        if(num < 1 || num > 9999){
            return "";
        }
        if (num < 1000){
            return num1To999(num);
        }
        String res = num1To9(num / 1000) + "千";
        int rest = num % 1000;
        if(rest == 0){
            return res;
        }else if(rest >= 100){
            res += num1To999(rest);
        }else {
            res += "零" + num1To99(rest,false);
        }
        return res;
    }

    public String num1To99999999(int num){
        if(num < 1 || num > 99999999){
            return "";
        }
        if (num < 10000){
            return num1To9999(num);
        }
        String res = num1To9999(num / 10000) + "万";
        int rest = num % 10000;
        if(rest == 0){
            return res;
        }else if(rest >= 1000){
            res += num1To9999(num);
        }else {
            res += num1To999(num);
        }
        return res;
    }
}

题目三十六:完全二叉树节点个数

求完全二叉树的节点的个数

分析:(遍历是可以的,但过于麻烦,时间复杂度高)

X为头节点的完全二叉树个数,先向左遍历,遍历到左树的最大深度,全局变量,再找到右树上最左节点的深度,进行判断,递归相加。

package class07;

/**
 * 求一颗完全二叉树节点的个数
 */
public class Code02_CBTNodeNum {

    class Node{
        int value;
        Node left;
        Node right;

        public Node(int value){this.value = value;}
    }

    public int getCBTNodeNum(Node head){
        if (head == null){
            return 0;
        }
        return process(head,1,mostleftLevel(head,1));
    }

    //以当前X为头节点的完全二叉树的个数
    // level 当前X节点再哪一层  height x左树的高度(不变)
    public int process(Node X,int level,int height){
        if(level == height){
            return 1;
        }
        if(mostleftLevel(X.right,level + 1) == height){ //X的右树的最左节点的深度和左树深度相同,说明X的左树是满二叉树
            // 以x为头节点的节点个数 = 2^(height - level) - 1 + 1 + ?(X的右树的节点个数)
            return (1 << (height - level)) + process(X.right,level + 1,height);
        } else { //X的右树的最左节点的深度和左树深度不同,说明X右树的右树是满二叉树
            // 以x为头节点的节点个数 = 2^(height - level - 1) - 1 + 1 + ?(X的左树的节点个数)
            return (1 << (height - level - 1)) + process(X.left,level + 1,height);
        }
    }

    //求以node为头的子树的最大深度,level表示node在整棵树的level层
    //以node为头的子树一定是完全二叉树
    public int mostleftLevel(Node node,int level){
        while (node != null){
            level++;
            node = node.left;
        }
        return level - 1;
    }
}

题目三十七:最长递增子序列(OlogN)

最长递增子序列问题

eg:[3,1,2,6,3,4] ->  [1,2,3,4]

分析:对于数组arr进行设计辅助数组dp,dp[i]表示子序列必须以arr[i]位置结尾的最长长度。

eg:arr=[3,1,2,6,3,4,0]

dp=[1,1,2,3,3,4,1]

package class07;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 * 最长递增子序列
 */
public class Code03_MaxIncreaseSubList {

    //分析:对于数组arr进行设计辅助数组dp,dp[i]表示子序列必须以arr[i]位置结尾的最长长度。
    //方法一: 时间复杂度O(N^2)
    public int[] getMaxIncreaseSubList(int[] arr){
        if(arr == null || arr.length == 0){
            return null;
        }
        int[] dp = getDp(arr); //获得辅助数组dp  O(N^2)
        int maxIndex = 0;
        for (int i = 1;i < dp.length;i++){
            if (dp[i - 1] < dp[i]){
                maxIndex = i;
            }
        }
        int N = dp[maxIndex];
        int[] subArr = new int[N];
        subArr[--N] = arr[maxIndex];
        for (int i = maxIndex - 1;i >= 0;i--){
            if (arr[i + 1] > arr[i]) {
                subArr[--N] = arr[i];
            }
        }
        return subArr;
    }

    //或得以每个位置结尾的最长递增子序列的个数
    public int[] getDp(int[] arr){
        int[] dp = new int[arr.length];
        dp[0] = 1;
        for (int i = 1;i < arr.length;i++){
            dp[i] = 1; //初始为1
            for (int j = i - 1;j >= 0;j--){ // 找比i位置小的数的个数
                if (arr[i] > arr[j]){
                    dp[i] = dp[j] + 1;
                    break;
                }
            }
        }
        return dp;
    }

    //方法二:时间复杂度O(NlogN)
    public int[] getMaxIncreaseSubList2(int[] arr){
        if(arr == null || arr.length == 0){
            return null;
        }
        //通过二分查找的方式,将辅助数组的时间复杂度降低到O(NlogN)
        int[] ends = new int[arr.length];  //存放i+1递增子序列的最小位
        ends[0] = arr[0];
        int len = 1;  //记录ends有效的数据长度
        //采用二分查找的方法查找arr[i]在ends中放在哪里
        for(int i = 1;i < arr.length;i++){
            if (arr[i] > ends[len - 1]){
                ends[len++] = arr[i];
            }else {
                int pos = binarySearch(ends,len,arr[i]);
                ends[pos] = arr[i];
            }
        }

        int[] res = new int[len];
        int index = arr.length - 1; //找到ends最后一个数,以该数向前搜索即可
        for(;index >= 0;index--){
            if (arr[index] == ends[len - 1]){
                break;
            }
        }
        res[--len] = ends[len];
        for (int i = index - 1;i >= 0 && len >= 0;i--){
            if (arr[i + 1] > arr[i]) {
                res[--len] = arr[i];
            }
        }
        return res;
    }

    //查找ends中  ends[i-1] < target end[i] > target
    public int binarySearch(int[] ends,int len,int target){
        int left = 0;
        int right = len - 1;
        while (left <= right){
            int mid = (left + right) / 2;
            if(ends[mid] > target){
                right = mid - 1;
            }else if (ends[mid] < target){
                left = mid + 1;
            }else {
                return mid;
            }
        }
        return left;
    }

    @Test
    public void test(){
        int[] arr = {3,1,2,6,3,4,0};
        int[] maxIncreaseSubList = getMaxIncreaseSubList2(arr);
        System.out.println(Arrays.toString(maxIncreaseSubList));
    }


}

题目三十八:被3整除

小Q得到一个神奇的数列:1,12,123,... 12345678910,1234567891011 ...

并且小Q对于能否被3整除这个性质很感兴趣

小Q现在希望你帮他计算一个从数列 的第l个到第r个(包含端点)右 多少个数可以被3整除。

输入描述:输入包括两个整数l和r(1<= l <= r <= 1e9),表示要求解的区间两端

输出描述:输出一个整数,表示区间内能被3整除的数字个数

(1034) % 3 等价于 (1+0+3+4)% 3

(10) % 3 等价于 (1+0)%3

所以只要把所有数加起来%3即可,123456789101112...99100.. % 3

等价于(1+2+3+4+5+6+7+8+9+1+0+1+1+1+2+1+3+1+4+...)%3

等价于(1+2+3+4+5+6+7+8+9+10+11+12+13+...+99+100)%3

所以就是等差数列求和%3  ((1+n)/2)(n-1+1)

package class07;

public class Code04_Mol3 {

    //数列:1,12,123,... 12345678910,1234567891011 ...
    // 1 <= left <= right <=1e9
    public int getMol_3(int left,int right){
        if(right < left){
            return 0;
        }
        int res = 0;
        while (left <= right){
            long sum = (1 + (left + 1)) * (left + 1) / 2;
            if (sum % 3 == 0){
                res++;
            }
            left++;
        }
        return res;
    }
}

题目三十九:查找未出现的整数

给定一个整数数组A,长度位n,有1<= A[i]<= n,且对于[1,n]的整数,其中部分整数会重复出现而部分不会出现。

实现算法找到[1,n]中所有未出现在A中的整数。

提示:尝试时间O(N)的时间复杂度和O(1)的空间复杂度(返回值不计入空间复杂度)

输入描述:一行数字,全部为整数,空格分隔

A0 A1 A2 A3 ...

输出描述:全部为整数,空格分隔

R0 R1 R2 R3

eg:输入: 1 3 4 3  输出  2

package class07;

/**
 * 实现算法找到[1,n]中所有未出现在A中的整数。
 */
public class FindEmptyNumber {
    
    public void printEmptyNumber(int[] arr){
        if (arr == null || arr.length == 0){
            return;
        }
        for (int value : arr){ //i位置上的数不是i+1,给它移到对应的位置
            modify(value,arr);
        }
        for (int i = 0;i < arr.length;i++){
            if(arr[i] != i + 1){
                System.out.print((i + 1) + "\t");
            }
        }
        System.out.println();
    }
    
    //对应数组i位置上放i+1
    public void modify(int value,int[] arr){
        while (arr[value - 1] != value){
            int temp = arr[value - 1];
            arr[value - 1] = value;
            value = temp;
        }
    }
}

题目四十:需要自己找边界条件的递归

CC里面有一个土豪很喜欢一位女直播KiKi唱歌,平时就经常给她点赞、送礼、私聊。最近CC直播平台在举行中秋之星主播歌唱比赛,假设一开始该女主播的初始人气值为start,能够晋升下一轮人气需要刚好达到end,土豪给主播增加人气的可以采取的方法有:

a.       点赞  花费x  C币,人气  +2

b.       送礼  花费y  C币,人气  *2

c.       私聊  花费z  C币,人气  -2

其中 end远大于start且end为偶数,请写一个程序帮助土豪计算以下,最少花费多少C币就能帮助该主播KiKi将人气刚好达到end,从而能够晋升下一轮?

输入描述:

第一行输入5个数据,分别为:x y z start end,每项数据以空格隔开

其中:0 < x,y,z <= 10000,0 < start,end <= 1000000

输出描述,需要花费的最少c币

eg:输入: 3 100 1 2 6     输出:6

package class07;

public class Code06_KiKi {
    
    //start end 为偶数
    public int getKiKi(int add,int times,int del,int start,int end){
        if(start > end){
            return -1;
        }
        return process(0,end,add,times,del,start,end * 2,((end - start) / 2) * add);
    }

    /**
     * 
     * @param preMoney   之前已经花了多少钱    可变
     * @param aim        目标   固定
     * @param add,times,del  固定参数 
     * @param cur         当前来到的人气
     * @param limitAim    人气大到什么程度不需要再尝试了   固定
     * @param limitCoin   已经使用的币大到什么程度不需要再尝试了  固定
     * @return
     */
    public int process(int preMoney,int aim,
                       int add,int times,int del,
                       int cur,
                       int limitAim,int limitCoin){
        if(preMoney > limitCoin){
            return Integer.MAX_VALUE;
        }
        if(cur < 0){
            return Integer.MAX_VALUE;
        }
        if(cur > limitAim){
            return Integer.MAX_VALUE;
        }
        if (aim == cur){
            return preMoney;
        }
        int min = Integer.MIN_VALUE;
        //人气 +2的方式
        int p1 = process(preMoney + add,aim,add,times,del,cur+2,limitAim,limitCoin);
        if(p1 != Integer.MAX_VALUE){
            min = p1;
        }
        //人气 *2的方式
        int p2 = process(preMoney + times,aim,add,times,del,cur*2,limitAim,limitCoin);
        if(p2 != Integer.MAX_VALUE){
            min = Math.min(min,p2);
        }
        //人气 -2的方式
        int p3 = process(preMoney + del,aim,add,times,del,cur-2,limitAim,limitCoin);
        if (p3 != Integer.MAX_VALUE){
            min = Math.min(min,p3);
        }
        return min;
    }
    
    //动态规划
    //两个可变参数  preMoney  cur
    public int minKiKi(int add,int times,int del,int start,int end){
        if (start > end){
            return -1;
        }
        int limitAim = end * 2;
        int limitCoin = ((end - start) / 2) * add;
        int[][] dp = new int[limitCoin + 1][limitAim + 1];
        for (int pre = 0;pre <= limitCoin;pre++){
            for (int aim = 0;aim <= limitAim;aim++){
                if (aim == start){
                    dp[pre][aim] = pre;
                }else {
                    dp[pre][aim] = Integer.MAX_VALUE;
                }
            }
        }
        for (int pre = limitCoin;pre >= 0;pre--){
            for (int aim = 0;aim <= limitAim;aim++){
                //人气值 +2的方式
                if (aim - 2 >= 0 && pre + add <= limitCoin){
                    dp[pre][aim] = Math.min(dp[pre][aim],dp[pre + add][aim - 2]);
                }
                //人气值 -2的方式
                if (aim + 2 <= limitAim && pre + del <= limitCoin){
                    dp[pre][aim] = Math.min(dp[pre][aim],dp[pre + del][aim + 2]);
                }
                //人气值 *2的方式
                if ((aim & 1) == 0){  //奇数不用判断,只需要判断偶数
                    if (aim / 2 >= 0 && pre + times <= limitCoin){
                        dp[pre][aim] = Math.min(dp[pre][aim],dp[pre + times][aim * 2]);
                    }
                }
            }
        }
        return dp[0][end];
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值