要求:跟定一个数的最大值,每次得到大还是小或者相等
思路:二分
要求:给定一个数,猜错要交钱,最小花多少钱
思路: dfs +memo,需要遍历小的数到大的数
找零钱问题
要求:找零钱,给定总额,给定货币的种类,输出最小的硬币个数
思路:这个跟数组可否恰好二分,背包问题本质上是同一种问题,就是可选可不选的状态
错误:初始化都是Integer.MAX_VALUE,然后对硬币进行遍历,没有对 i - j 判断是否是Integer.MAX_VALUE
ac
要求:与海盗分金子题目要求类似,一堆金子两头取
思路:也可以采用Math.min();The dp[i][j] saves how much more scores that the first-in-action player will get from i to j than the second player. First-in-action means whomever moves first. You can still make the code even shorter but I think it looks clean in this way.
// 海盗分金子的问题
public boolean PredictTheWinner(int[] nums) {
if (nums == null || nums.length <= 1) return true;
int[] sum = new int[nums.length + 1];
sum[0] = 0;
int len = nums.length;
for (int i = 1; i <= len; i++) {
sum[i] = sum[i - 1] + nums[i - 1];
}
int[][] dp = new int[len + 1][len + 1];
for (int i = 1; i <= len; i++) {
dp[i][i] = nums[i - 1];
}
for (int l = 1; l < len; l++) {
for (int i = 1; i <= len - l; i++) {
int j = i + l;
dp[i][j] = sum[j] - sum[i - 1] - Math.min(dp[i + 1][j] , dp[i][j - 1]);
}
}
return 2 * dp[1][len] >= sum[len];
}
//海盗分金问题的另外一个解法
public static int getMoney(int[] nums) {
// if
int m = nums.length;
int[][][] memo = new int[m + 1][m + 1][2];
int[] resu = dfs(memo,nums, 0, m - 1, 0 );
System.out.println(Arrays.toString(resu));
return resu[0];
}
public static int[] dfs(int[][][] memo, int [] nums, int le, int ri, int num) {
int[] resu = new int[2];
if (le == ri) {
if (num == 0) {
resu[0] = nums[le];
resu[1] = 0;
} else {
resu[0] = 0;
resu[1] = nums[le];
}
return resu;
}
if (memo[le][ri][num] > 0) {
return memo[le][ri];
}
if (num == 0) {
int temp1[] = dfs(memo, nums, le + 1, ri, 1);
int max1 = temp1[0] + nums[le];
int temp2[] = dfs(memo, nums, le, ri - 1, 1);
int max2 = temp1[0] + nums[ri];
if (max1 > max2) {
temp1[0] = temp1[0] + nums[le];
memo[le][ri] = temp1;
return temp1;
} else {
temp2[0] = temp2[0] + nums[ri];
memo[le][ri] = temp2;
return temp2;
}
}
if (num == 1) {
int temp1[] = dfs(memo, nums, le + 1, ri, 0);
int max1 = temp1[1] + nums[le];
int temp2[] = dfs(memo, nums, le, ri - 1, 0);
int max2 = temp1[1] + nums[ri];
if (max1 > max2) {
temp1[1] = temp1[1] + nums[le];
memo[le][ri] = temp1;
return temp1;
} else {
temp2[1] = temp2[1] + nums[ri];
memo[le][ri] = temp2;
return temp2;
}
}
return null;
}
思路:dfs加缓存,注意本次调用help函数,要取反
要求:给定0的个数以及1的个数,判定最多可以选择的组成的字符串的个数
思路:这个是0-1背包问题的类似问题,其实考试那个也是0- 1背包问题,这个是复杂版本的,背包定义dp[i][j],代表的是如果前i个占用j大的空间,转移方程为用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:
f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}
此处定义的dp[i][j]为占用的0 与1的个数,这里面有两个限制参数,时间复杂度为O(n立方)
// 背包问题,都是从包来进行考虑的
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m+1][n+1];
for (String s : strs) {
int[] count = count(s);
for (int i=m;i>=count[0];i--)
for (int j=n;j>=count[1];j--)
dp[i][j] = Math.max(1 + dp[i-count[0]][j-count[1]], dp[i][j]);
}
return dp[m][n];
}
public int[] count(String str) {
int[] res = new int[2];
for (int i=0;i<str.length();i++)
res[str.charAt(i) - '0']++;
return res;
}
要求:数组里面元素的和能否组成一个特定的数
思路:可以优化的背包问题的
// 核心代码, 比上面少了一维度
for (int n : nums) {
for (int i = sum; i>= n; i--) {
dp[i] = dp[i] || dp[i - n];
}
}
要求:给定一个三角形,输出从根节点到叶子节点的类累加和最小的值
思路:三角形动态规划问题,如果涉及到路径的问题,可以采用pre缓存
每一个子节点从两个父节点继承
要求:输出摇摆子序列的长度
思路:缓存两个, up, down,初始化的时候up和down都记为1,然后上升跟新up,下降更新do
输出:up和do大的那个
要求:给定一个数n,求1到n的n个数,可以组成多少种完全不同的二叉查找树
思路:G(n) = F(1, n) + F(2, n) + … + F(n, n). G(0)=1, G(1)=1. F(i, n) = G(i-1) * G(n-i) 1 <= i <= n
F 函数表示为以第i个结点为根节点,n个数的而查找树的个数 G(n) = G(0) * G(n-1) + G(1) * G(n-2) + … + G(n-1) * G(0)
ac:dp,注意下下标的范围,一般来讲,根据简单的demo进行测试即可
要求:给定一个数n,输出组成不同的二叉查找树
思路:跟上一个 一样 采用 dfs,需要注意的是, 函数返回的是树的集合
要求:给定目标值,数组里面有多少种可能结果集合
思路;这个是完全背包问题,与coinchange类似,只不过转移方程一个是最小的硬币数量,一个是最多的组合数量,不是0,1背包的问题 01背包,物品在外, 完全背包, 目标值在外(cionchange)
要求:给定目标值,得出所有无重复的,数组中数的和等于当前值的目标值
思路:dfs+ backtracking, 一般结果输出型的,都得利用backtracking,
trick:因为是数,并且要求结果无重复,就要放一个start,下一次只能从start以及start的右侧进行选取,否则会出现重复
一般的题目,都是拐一个弯。注意下trick点,对比。