题目三十一:二叉树的递归套路(树形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 BST的size)
右子树(maxBSTHead BST的size)
与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];
}
}