斐波那契数
https://leetcode.cn/problems/fibonacci-number/submissions/
这种递归很容易想到,就可以直接得出动态方程
class Solution {
public int fib(int n) {
return func(n);
}
public int func(int n){
if (n == 0){
return 0;
}
if (n == 1){
return 1;
}
return func(n - 1) + func(n - 2);
}
public int dp(int n){
if (n == 0){
return 0;
}
if (n == 1){
return 1;
}
int[] nums = new int[n + 1];
nums[0] = 0;
nums[1] = 1;
for (int i = 2; i < nums.length; i++) {
nums[i] = nums[i - 1] + nums[i - 2];
}
return nums[nums.length -1];
}
}
爬楼梯
https://leetcode.cn/problems/climbing-stairs/
与上面那题差不多就不展示了。
最小代价楼梯
https://leetcode.cn/problems/min-cost-climbing-stairs/submissions/
思路:步步最优递归的规则,从最后三步开始,最后两步肯定是可以直接走完,那么我们在倒数第三步就可以选择小的那种情况看走1步还是2步。一直递归就可。
也是有递归再动态规划,但是在题有时间的限制,递归过不了,我们就必须改为动态规划。
class Solution1 {
//递归超时,我们可以尝试动态规划
public int minCostClimbingStairs(int[] cost) {
if(cost.length <= 2){
return cost[0] > cost[1] ? cost[1] : cost[0];
}
//选择0 1开,选择走一步或者两步
return Math.min(func(cost,0),func(cost,1));
}
public int func(int[] cost,int index){
if (index == cost.length - 1 || index == cost.length - 2){
return cost[index];
}
return Math.min(cost[index] + func(cost,index + 1),cost[index] +func(cost,index + 2));
}
public int minCostClimbingStairsByDp(int[] cost){
if(cost.length <= 2){
return cost[0] > cost[1] ? cost[1] : cost[0];
}
int[] dp = new int[cost.length];
dp[cost.length - 1] = cost[cost.length - 1];
dp[cost.length - 2] = cost[cost.length - 2];
for (int i = dp.length - 3; i >= 0; i--) {
dp[i] = Math.min(cost[i] + dp[i + 1],cost[i] + dp[i + 2]);
}
return dp[0] > dp[1] ? dp[1] : dp[0];
}
}
不同路劲
https://leetcode.cn/problems/unique-paths-i/
思路:只能向下和向右走两种可能,但他来到右边和下边的边界,那么他就只能一直走也就是只有一条路,递归思路很简单
class Solution10 {
//m是y n是x
public int uniquePaths(int m, int n) {
//一条线只有一种可能
if (m == 1 || n == 1){
return 1;
}
return func(m,n,0,0);
}
public int func(int m,int n,int x,int y){
if (m == y + 1 || n == x + 1){
return 1;
}
return func(m,n,x + 1,y)
+ func(m,n,x,y + 1);
}
public int dp(int m,int n){
if (m == 1 || n == 1){
return 1;
}
int[][] dp = new int[m][n];
for (int i = 0; i < dp[0].length; i++) {
dp[dp.length - 1][i] = 1;
}
for (int i = 0; i < dp.length; i++) {
dp[i][dp[0].length - 1] = 1;
}
for (int i = dp.length - 2; i >= 0; i--) {
for (int j = dp[0].length - 2; j >= 0; j--) {
dp[i][j] = dp[i + 1][j] + dp[i][j + 1];
}
}
return dp[0][0];
}
}
不同路劲2
https://leetcode.cn/problems/unique-paths-ii/
思路:这个和上一个差不多,只是多个障碍,所以难度也稍微大了一点,
这个在最后不能直接右边界和下边界取1,因为他有可能在前面还有障碍导致这条路走不通。
class Solution11 {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
if (obstacleGrid[0][0] == 1){
return 0;
}
return func(obstacleGrid,0,0);
}
//递归
public int func(int[][] nums,int m,int n){
//到达
if (m == nums.length - 1 && n == nums[0].length - 1){
return 1;
}
//在下边界和右边界
if (m == nums.length - 1 && n < nums[0].length - 1){
if (nums[m][n + 1] == 1){
return 0;
}else {
return func(nums, m, n + 1);
}
}
if (m < nums.length - 1 && n == nums[0].length - 1){
if (nums[m + 1][n] == 1){
return 0;
}else {
return func(nums, m + 1, n);
}
}
if (nums[m + 1][n] == 1 && nums[m][n + 1] == 1){
return 0;
}
if (nums[m + 1][n] == 1){
return func(nums, m, n + 1);
}
if (nums[m][n + 1] == 1){
return func(nums, m + 1, n);
}
return func(nums, m, n + 1)
+ func(nums, m + 1, n);
}
//动态规划
public int dp(int[][] nums){
if (nums[0][0] == 1){
return 0;
}
int[][] dp = new int[nums.length][nums[0].length];
//有可能在最后一行或者一列,其中有一个有障碍,那么我们就取他后面的为1
int hang = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i][nums[0].length - 1] == 1){
hang = i;
}
}
for (int i = hang; i < nums.length; i++) {
dp[i][nums[0].length - 1] = 1;
}
hang = 0;
for (int i = 0; i < nums[0].length; i++) {
if (nums[nums.length - 1][i] == 1){
hang = i;
}
}
for (int i = hang; i < nums[0].length; i++) {
dp[nums.length - 1][i] = 1;
}
for (int i = dp.length - 2; i >= 0; i--) {
for (int j = dp[0].length - 2; j >= 0; j--) {
if (nums[i + 1][j] == 1 && nums[i][j + 1] == 0){
dp[i][j] = dp[i][j + 1];
}
else if(nums[i][j + 1] == 1 && nums[i + 1][j] == 0){
dp[i][j] = dp[i + 1][j];
}
else if (nums[i][j + 1] == 1 && nums[i + 1][j] == 1){
dp[i][j] = 0;
} else {
dp[i][j] = dp[i + 1][j] + dp[i][j + 1];
}
}
}
return dp[0][0];
}
}
不同路劲3
https://leetcode.cn/problems/unique-paths-iii/
如果对上面两题很熟悉,那么这题也会很简单,但是他这里要走过所有是1的格子,所以最好在返回结果的时候要对结果进行遍历判断。
而且这题是可以进行上下左右走的,所以递归次数也就多了,对于这题的话可能数量样本较小,递归的时间超过了100%,也就没有写
动态规划了。
class Solution12 {
public int uniquePathsIII(int[][] grid) {
int[][] startAndEnd = getStartAndEnd(grid);
boolean[][] isGone = new boolean[grid.length][grid[0].length];
for (int i = 0; i < isGone.length; i++) {
for (int j = 0; j < isGone[0].length; j++) {
if (grid[i][j] == -1){
isGone[i][j] = true;
}
}
}
return func(grid,startAndEnd[0][0],startAndEnd[0][1],startAndEnd[1][0],startAndEnd[1][1],isGone);
}
public int func(int[][] grid,int y,int x,int resY,int resX,boolean[][] isGone){
if (!inArea(grid,y,x) || grid[y][x] == -1 || isGone[y][x]){
return 0;
}
if (grid[y][x] == 2){
boolean a = true;
isGone[resY][resX] = true;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (!isGone[i][j]){
a = false;
break;
}
}
}
isGone[resY][resX] = false;
if (!a){
return 0;
}
return 1;
}
isGone[y][x] = true;
int sum = func(grid, y - 1, x, resY, resX, isGone)
+ func(grid, y + 1, x, resY, resX, isGone)
+ func(grid, y, x - 1, resY, resX, isGone)
+ func(grid, y, x + 1, resY, resX, isGone);
//回溯撤销
isGone[y][x] = false;
return sum;
}
//是否在范围内
public boolean inArea(int[][] grid,int y,int x){
if (x < 0 || y < 0
|| x >= grid[0].length || y >= grid.length){
return false;
}else {
return true;
}
}
//获取出发点和结束点 出发点(res[0][0],res[0][1]) 结束点(res[1][0],res[1][1])
public int[][] getStartAndEnd(int[][] nums){
int[][] res = new int[2][2];
for (int i = 0; i < nums.length; i++) {
for (int j = 0; j < nums[0].length; j++) {
if (nums[i][j] == 1){
res[0][0] = i;
res[0][1] = j;
}
if (nums[i][j] == 2){
res[1][0] = i;
res[1][1] = j;
}
}
}
return res;
}
}
整数的拆分
https://leetcode.cn/problems/integer-break/submissions/
这里是有可以大于3就可以把它们拆开,比如2拆开肯定是1*1比原来小,就没有必要拆了
,比如3拆开2*1也比3小也就没必要拆了。
这里的递归严重超时,所以必须使用动态规划。
class Solution3 {
public int integerBreak(int n) {
//为了满足k>=2,而后面2和3都是不需要分的因为2分了只能是1*1,3分了只能是2*1
if (n == 2){
return 1;
}
if (n == 3){
return 2;
}
return func(n);
}
public int func(int n){
//2再分的话就变成1了那就会更加小了
if (n == 2 || n == 3){
return n;
}
//这几种情况不用分了,以为再分n 就小于2了
int max = 1;
//递归次数可以判断出为n/2 比如4
for (int i = 0; i < n/2; i++) {
//如果下面两个数还可以再分就继续递归 (i+1)*(n - i - 1)
int n1 = func(i + 1);
int n2 = func(n - i - 1);
max = Math.max(max,n1*n2);
}
return max;
}
//动态规划
public int dp(int n){
int[] dp = new int[n];
if (n == 2){
return 1;
}
if (n == 3){
return 2;
}
dp[0] = 1;
dp[1] = 2;
dp[2] = 3;
for (int i = 3; i < dp.length; i++) {
for (int j = 0; j < (i + 1)/2; j++) {
dp[i] = Math.max(dp[j]* dp[i - j - 1],dp[i]);
}
}
return dp[n - 1];
}
}
不同的二叉搜索树
https://leetcode.cn/problems/unique-binary-search-trees/submissions/
这里也是主要从左右子树获取它们的次数,每次节点个数-1
递归方式的话刚好不超时,可以用,但是5%击败率就有点低,动态规划的话就100%。
class Solution4 {
public int numTrees(int n) {
return func2(n);
}
//实现一个子树能够有多少分支
public int func2(int n){
if (n == 1 || n == 0){
return 1;
}
if (n == 2){
return n;
}
int sum = 0;
for (int i = 0; i < n; i++) {
sum+= func2(i) * func2(n - 1 - i);
}
return sum;
}
public int dp(int n){
if (n == 1 || n == 0){
return 1;
}
if (n == 2){
return n;
}
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i <= n; i++) {
for (int j = 0; j < i; j++) {
dp[i] += dp[j] * dp[i - 1 - j];
}
}
return dp[n];
}
}
分割和子集问题
https://leetcode.cn/problems/partition-equal-subset-sum/
额,这道题开始优点卡主了不知道怎么用初始化dp数组,我用另一种方式,也刚好通过了,就是把所有sum的可能都加到set中,直接看target在不在就行了。
public boolean canPartition(int[] nums) {
if (nums.length == 1) { // 只有一个,无法分解
return false;
}
int sum = 0;
for (int i=0;i<nums.length;i++) {
sum += nums[i];
}
if (sum % 2 == 1) { // 和不能是奇数
return false;
}
int target = sum / 2;
Set<Integer> cacheSum = new HashSet(); // 存储所有可能的和
for (int num : nums) {
Set<Integer> tmpSum = new HashSet();
if (num == target) {
return true;
}
if (num < target) {
tmpSum.add(num);
}
for (int historySum : cacheSum) {
int sum2 = historySum + num;
if (sum2 == target) {
return true;
}
if (sum2 < target) {
tmpSum.add(sum2);
}
}
cacheSum.addAll(tmpSum);
}
return false;
}
下面是动态规划的,这里的递归严重超时
这里有一个典型的背景,就是0和1背包问题
class Solution5 {
//二维数组
public boolean dp(int[] nums){
if (nums.length == 1){
return false;
}
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
if(sum % 2 == 1){
return false;
}
int target = sum / 2;
//如果为奇数的话不可能得出结果
boolean[][] dp = new boolean[nums.length][target + 1];
//现在就是完整的01背包问题了 装target个先初始化第一行 因为第一个数字如果小于target的话,肯定可以把他给装满
if (nums[0] <= target){
dp[0][nums[0]] = true;
}
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < dp[0].length; j++) {
if (j > nums[i]){
//看看上一行有无可以加起来满足这个背包的
dp[i][j] = dp[i - 1][j] | dp[i - 1][j - nums[i]];
}else {
//如果之前就已经成功的就直接赋值
dp[i][j] = dp[i - 1][j] | dp[i][j];//相当dp[i][j] = dp[i - 1][j];
}
}
}
return dp[nums.length - 1][target];
}
//一维数组
public boolean dp2(int[] nums) {
if (nums.length == 1){
return false;
}
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
if(sum % 2 == 1){
return false;
}
int target = sum / 2;
//如果为奇数的话不可能得出结果
boolean[] dp = new boolean[target + 1];
//现在就是完整的01背包问题了 装target个先初始化第一行 因为第一个数字如果小于target的话,肯定可以把他给装满
if (nums[0] <= target){
dp[nums[0]] = true;
}
for (int i = 1; i < nums.length; i++) {
for (int j = dp.length - 1; j >= nums[i]; j--) {
//如果当前背包能装的数量大于他,看看前面有无能够装的
//看看上一行有无可以加起来满足这个背包的
dp[j] = dp[j] | dp[j - nums[i]];
}
}
return dp[target];
}
最后一块石头重量2
https://leetcode.cn/problems/last-stone-weight-ii/submissions/
思路和上面那题差不多,也是把求总和,分成两堆,两堆最接近一半,可以直接得出答案。使用一位数组优化较大
class Solution7 {
//二维数组
public int lastStoneWeightII(int[] stones) {
if (stones.length == 1){
return stones[0];
}
int sum = 0;
for (int i = 0; i < stones.length; i++) {
sum += stones[i];
}
int target = sum / 2;
int[][] dp = new int[stones.length][target + 1];
for (int i = 0; i < target + 1; i++) {
if (i >= stones[0]){
dp[0][i] = stones[0];
}
}
for (int i = 1; i < stones.length; i++) {
for (int j = 0; j < dp[0].length; j++) {
if (j < stones[i]){
dp[i][j] = dp[i - 1][j];
}else {
dp[i][j] = Math.max(dp[i - 1][j],dp[i - 1][j - stones[i]] + stones[i]);
}
}
}
return sum - target - dp[stones.length - 1][target];
}
//一维滚动数组
public int lastStoneWeightII2(int[] stones) {
if (stones.length == 1){
return stones[0];
}
int sum = 0;
for (int i = 0; i < stones.length; i++) {
sum += stones[i];
}
int target = sum / 2;
int[] dp = new int[target + 1];
for (int i = 0; i < stones.length; i++) {
for (int j = dp.length - 1; j >= stones[i]; j --) {
dp[j] = Math.max(dp[j],dp[j - stones[i]] + stones[i]);
}
}
return sum - dp[target] * 2;
}
}
最后一块石头重量1
https://leetcode.cn/problems/last-stone-weight-i/submissions/
直接使用大根堆能快速解决
class Solution6 {
public int lastStoneWeight(int[] stones) {
if (stones.length == 1){
return stones[0];
}
PriorityQueue<Integer> integers = new PriorityQueue<>(stones.length, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
for (int i = 0; i < stones.length; i++) {
integers.add(stones[i]);
}
while (integers.size() >= 2){
Integer n1 = integers.poll();
Integer n2 = integers.poll();
integers.add(Math.abs(n1 - n2));
}
return integers.poll();
}
}
目标和
https://leetcode.cn/problems/target-sum/
如果这题使用递归的话就会很简单,而且题目也能过,如果在竞赛之类等短时间内的建议先用递归这种思路简单,时间快速的方法。
本题的话,也只有两种可能,一种是加这个数,一种是减这个数,自己画一下递归树就可以。
int res = 0;
public int findTargetSumWays2(int[] nums, int target) {
if (nums.length == 1){
if (nums[0] == Math.abs(target))
return 1;
else
return 0;
}
func(nums,target,0,0);
return res;
}
public void func(int[] nums,int target,int index,int sum){
if (index == nums.length){
if (sum == target){
res++;
}
return;
}
func(nums, target, index + 1, sum + nums[index]);
func(nums, target, index + 1, sum - nums[index]);
}
下面是我自己的笨动态规划,和官方的差挺多的,时间肯定比暴力递归的要快很多,一个是6ms一个500ms,但是空间复杂度要高一点。
当然还可以进行优化。
public int findTargetSumWays(int[] nums, int target) {
if (nums.length == 1){
if (nums[0] == Math.abs(target))
return 1;
else
return 0;
}
int sum = 0;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
if (target > sum){
return 0;
}
int[][] dp = new int[nums.length][sum * 2 + 1];
for (int i = dp[0].length / 2,j = sum - 1; i >= 0; i--,j--) {
if (nums[0] == sum - i){
if (nums[0] == 0){
dp[0][i] = 2;
}else {
dp[0][i]++;
}
}
}
for (int i = dp[0].length / 2 + 1; i <= dp[0].length; i++) {
if (nums[0] == i - sum){
if (nums[0] == 0){
dp[0][i] = 2;
}else {
dp[0][i]++;
}
}
}
for (int i = 0; i < dp.length - 1; i++) {
for (int j = 0; j < dp[0].length; j++) {
if (dp[i][j] != 0){
if (j - nums[i + 1] >= 0){
dp[i + 1][j - nums[i + 1]] += dp[i][j];
}
if (j + nums[i + 1] <= dp[0].length - 1) {
dp[i + 1][j + nums[i + 1]] += dp[i][j];
}
}
}
}
return dp[nums.length - 1][sum + target];
}
最终优化
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for (int i = 0; i < nums.length; i++) sum += nums[i];
if ((target + sum) % 2 != 0) return 0;
int size = (target + sum) / 2;
if(size < 0) size = -size;
int[] dp = new int[size + 1];
dp[0] = 1;
for (int i = 0; i < nums.length; i++) {
for (int j = size; j >= nums[i]; j--) {
dp[j] += dp[j - nums[i]];
}
}
return dp[size];
}
}
不考虑nums[i]的情况下,填满容量为j的背包,有dp[j]种方法。
那么考虑nums[i]的话(只要搞到nums[i]),凑成dp[j]就有dp[j - nums[i]] 种方法。
例如:dp[j],j 为5,
- 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 dp[5]。
- 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 dp[5]。
- 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 dp[5]
- 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 dp[5]
- 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 dp[5]
dp[j] += dp[j - nums[i]]
一和零
https://leetcode.cn/problems/ones-and-zeroes/
这里我开始是使用了三维数组的dp,效率确实比较慢,而且代码也比较难度,过程确实比上面的一维和二维要难
public int findMaxForm(String[] strs, int m, int n) {
int[][][] dp = new int[strs.length][m + 1][n + 1];
int[] mAndN = getMAndN(strs[0]);
//初始化 第一个的
for (int i = mAndN[0]; i <= m; i++) {
for (int j = mAndN[1]; j <= n; j++) {
dp[0][i][j] = 1;
}
}
for (int i = 1; i <= strs.length - 1; i++) {
int[] mAndN1 = getMAndN(strs[i]);
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= n; k++) {
if (k < mAndN1[1] || j < mAndN1[0]){
dp[i][j][k] = dp[i - 1][j][k];
}else {
dp[i][j][k] = Math.max(dp[i - 1][j][k],dp[i - 1][j - mAndN1[0]][k - mAndN1[1]] + 1);
}
}
}
}
return dp[strs.length - 1][m][n];
}
public int[] getMAndN(String str){
char[] chars = str.toCharArray();
int m = 0;
int n = 0;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == '1'){
n++;
}else {
m++;
}
}
return new int[]{m,n};
}
下面是我自己进行二维优化的
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m + 1][n + 1];
int[] mAndN = getMAndN(strs[0]);
//初始化 第一个的
for (int i = mAndN[0]; i <= m; i++) {
for (int j = mAndN[1]; j <= n; j++) {
dp[i][j] = 1;
}
}
for (int i = 1; i <= strs.length - 1; i++) {
int[] mAndN1 = getMAndN(strs[i]);
for (int j = m; j >= mAndN1[0]; j--) {
for (int k = n; k >= mAndN1[1]; k--) {
if (k >= mAndN1[1] && j >= mAndN1[0]){
dp[j][k] = Math.max(dp[j][k],dp[j - mAndN1[0]][k - mAndN1[1]] + 1);
}
}
}
}
return dp[m][n];
}
public int[] getMAndN(String str){
char[] chars = str.toCharArray();
int m = 0;
int n = 0;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == '1'){
n++;
}else {
m++;
}
}
return new int[]{m,n};
}
}
兑换零钱1
https://leetcode.cn/problems/coin-change/submissions/
class Solution9 {
HashMap<Integer,Integer> map = new HashMap();
public int coinChange(int[] coins, int amount) {
return func(coins,amount);
}
public int func(int[] coins ,int resM){
if (resM == 0){
return 0;
}
if (resM < 0){
return -1;
}
if (map.containsKey(resM)){
return map.get(resM);
}
int min = Integer.MAX_VALUE;
for (int i = 0; i < coins.length; i++) {
int res = func(coins,resM - coins[i]);
if (res >= 0 && res < min){
min = res + 1;
}
}
map.put(resM,(min == Integer.MAX_VALUE) ? -1 : min);
return map.get(resM);
}
public int dp(int[] coins ,int amount){
if (amount == 0){
return 0;
}
int[] dp = new int[amount + 1];
for (int i = 0; i < dp.length; i++) {
dp[i] = Integer.MAX_VALUE;
}
dp[0] = 0;
for (int i = 0; i < coins.length; i++) {
for (int j = coins[i]; j < amount + 1; j++) {
if (dp[j - coins[i]] != Integer.MAX_VALUE){
dp[j] = Math.min(dp[j],dp[j - coins[i]] + 1);
}
}
}
return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
}
}
兑换零钱2
https://leetcode.cn/problems/coin-change-2/submissions/
class Solution10 {
public int change(int amount, int[] coins) {
return func(coins,amount,amount,0);
}
//递归暴力搜索
public int func(int[] coins,int resA,int target,int index){
if (resA == 0){
return 1;
}
if (resA < 0 ){
return 0;
}
//减枝条
if (index >= coins.length || coins[index] > resA){
return 0;
}
int sum = 0;
for (int i = 0,j = 0; i <= resA; i+= coins[index],j++) {
sum += func(coins,resA - coins[index] * j,target,index + 1);
}
return sum;
}
public int dp(int amount, int[] coins){
//1,创建数组
int[] dp = new int[amount + 1];
//初始化 个体amount为0的都设置为1,因为都只有一种可能就是不用加,然后第一行就是个体第一个硬币设置
dp[0] = 1;
for (int i = 0; i < coins.length; i++) {
for (int j = coins[i]; j < dp.length; j++) {
dp[j] += dp[j - coins[i]];
}
}
return dp[amount];
}
}
完全平方数
https://leetcode.cn/problems/perfect-squares/
class Solution12 {
public int numSquares(int n) {
int[] nums = new int[(int) Math.sqrt(n)];
for (int i = 0; i < nums.length; i++) {
nums[i] = i + 1;
}
int[] dp = new int[n + 1];
dp[0] = 0;
for (int i = 1; i < dp.length; i++) {
dp[i] = Integer.MAX_VALUE;
}
for (int i = 0; i < nums.length; i++) {
int num = (int) Math.pow(nums[i],2);
for (int j =num; j < dp.length; j++) {
dp[j] = Math.min(dp[j],dp[j - num] + 1);
}
}
return dp[n];
}
//下面是递归暴力搜索
public int numSquares2(int n){
int[] nums = new int[(int) Math.sqrt(n)];
for (int i = 0; i < nums.length; i++) {
nums[i] = i + 1;
}
return func(nums,n,0,0);
}
public int func(int[] nums,int n,int nowNum,int numNum){
if (nowNum > n){
return -1;
}
if (nowNum == n){
return numNum;
}
int min = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
int num = (int)Math.pow(nums[i],2);
int func = func(nums, n, nowNum + num,numNum + 1);
//减枝
if (func == -1){
break;
}else {
min = Math.min(min,func);
}
}
return min;
}
}
打家劫舍1
递归和动态规划
class Solution241 {
public int rob1(int[] nums) {
if (nums.length == 1){
return nums[0];
}
int[] dp = new int[nums.length];
//第一间房间最大只能偷自己
dp[0] = nums[0];
dp[1] = nums[0] > nums[1] ? nums[0] : nums[1];
for (int i = 2; i < nums.length - 1; i++) {
dp[i] = Math.max(dp[i - 2] + nums[i],dp[i - 1]);
}
return dp[dp.length - 1];
}
public int rob(int[] nums) {
if (nums.length == 1){
return nums[0];
}
if (nums.length == 2){
return nums[0] > nums[1] ? nums[0] : nums[1];
}
int func = func(nums, 0, nums[0]);
func = Math.max(func,func(nums,1,nums[1]));
return func;
}
public int func(int[] nums,int index,int sum){
if (index == nums.length - 1 || index == nums.length - 2){
return sum;
}
if (index > nums.length - 1){
return sum;
}
//走i + 2 走 i + 3
int max = func(nums, index + 2, sum + nums[index + 2]);
if (index + 3 <= nums.length - 1){
max = Math.max(func(nums, index + 3, sum + nums[index + 3]),max);
}
return max;
}
}
买入股票最佳时机1
class Solution14 {
public int maxProfit(int[] prices) {
func(prices,0,0,0);
return max;
}
int max = 0;
//是否买入
public void func(int[] prices,int index,int isBuy,int sum){
if (index == prices.length){
max = Math.max(max,sum);
return;
}
//如果买入了 看看今天是不是最大 然后继续比下一个
if (isBuy == 1){
max = Math.max(max,sum + prices[index]);
func(prices, index + 1, isBuy, sum);
return;
}
//如果没买入 可以选择今天买,也可以选择不买
func(prices, index + 1, 1, sum - prices[index]);
func(prices, index + 1, isBuy, sum );
}
public int dp(int[] prices){
//dp[0][i] 第i天买入的最低价格 dp[1][i] 第i天卖出的最大利润
int[][] dp = new int[2][prices.length];
dp[0][0] = prices[0];
for (int i = 1; i < dp[0].length; i++) {
dp[0][i] = Math.min(dp[0][i - 1],prices[i]);
dp[1][i] = Math.max(dp[1][i - 1],prices[i] - dp[0][i - 1]);
}
return dp[1][prices.length - 1];
}
}
nc(nums, index + 2, sum + nums[index + 2]);
if (index + 3 <= nums.length - 1){
max = Math.max(func(nums, index + 3, sum + nums[index + 3]),max);
}
return max;
}
}
买入股票最佳时机1
```java
class Solution14 {
public int maxProfit(int[] prices) {
func(prices,0,0,0);
return max;
}
int max = 0;
//是否买入
public void func(int[] prices,int index,int isBuy,int sum){
if (index == prices.length){
max = Math.max(max,sum);
return;
}
//如果买入了 看看今天是不是最大 然后继续比下一个
if (isBuy == 1){
max = Math.max(max,sum + prices[index]);
func(prices, index + 1, isBuy, sum);
return;
}
//如果没买入 可以选择今天买,也可以选择不买
func(prices, index + 1, 1, sum - prices[index]);
func(prices, index + 1, isBuy, sum );
}
public int dp(int[] prices){
//dp[0][i] 第i天买入的最低价格 dp[1][i] 第i天卖出的最大利润
int[][] dp = new int[2][prices.length];
dp[0][0] = prices[0];
for (int i = 1; i < dp[0].length; i++) {
dp[0][i] = Math.min(dp[0][i - 1],prices[i]);
dp[1][i] = Math.max(dp[1][i - 1],prices[i] - dp[0][i - 1]);
}
return dp[1][prices.length - 1];
}
}