1.确定数组维数,如果是一维数组行形式,千万不要复杂化问题;
2.注意思考问题无思路时,有限考虑倒数第二部并设为x;
3.子序列问题就想以每一个位置为结尾
4.最值问题不用思考逻辑,只要分类使用min,max就行
5.问题第一步是确定维度
6.二维动态规划明确i、j遍历顺序时,观察公式,进行逻辑演算推论
7.流程=几维变量=判断是什么型=推公式=演绎一个(判断先走x还是先走y,有些不影响可以随便写,有些有影响)=边界条件
8.坐标性dp-》坐标,以i结尾 /序列性dp-》前i个/划分型-》前i个,推得时候利用j满足条件/区间型f(i,j)从i到j/双序列型 ,两个字符串
9.博弈动态规划从第一步开始
10.背包问题中,数组大小和总承重有关
例题
###1左上角走到右下角有多少种走法o(n^2)
public static int findMIn(int m,int n){
int[][] f = new int[m+1][n+1];
f[0][0]=0;
f[0][1]=1;
f[1][0]=1;
for(int i = 1;i for(int j =1;j
f[i][j]= f[i-1][j]+f[i][j-1]; } } return f[m][n]; } ###2青蛙跳问题,求是否能跳到o(n^2) public static boolean findhow(int[] bu){ int len =bu.length; boolean[] f = new boolean[len]; f[0]=true; for(int i =1;i for(int j=0;j
if(f[j]==true && i-j f[i] = true; } } } return f[len-1]; } ###3扔鸡蛋问题 o(n^2*k) public static int eggs(int lou,int dan){ int[][] f = new int[lou+1][dan+1]; for(int a =1;a<=lou;a++){ f[a][1] = a; } for(int b =1;b<=dan;b++){ f[1][b] = 1; } for(int i = 2;i<=lou;i++){ for(int j =2;j<=dan;j++){ f[i][j] =Integer.MAX_VALUE; for(int k=1;k
f[i][j] = Math.min(f[i][j],Math.max(f[k-1][j-1]+1,f[i-k][j]+1)); } } } return f[lou][dan]; } ###4刷房子问题 o(n^2*k) if(costs==null||costs.length==0||(costs.length==1&&costs[0].length==0)){ return 0; } //costs = rever(costs); int y = costs[0].length; int x = costs.length; int f[][] =new int[x][y]; for(int i=0;i f[i][0] = costs[i][0]; } for(int j =1;j //看谁先,要是竖着填就是先遍历y,要是横着填就是先遍历x for(int i =0;i int res=Integer.MAX_VALUE; //二维情况下,如果还有一个变量需要控制则需要再加入k for(int k =0;k if(k!=i){ res = Math.min(res,f[k][j-1]); } } f[i][j]=res+costs[i][j]; } } int res =Integer.MAX_VALUE; for(int i=0;i res=Math.min(f[i][y-1],res); } return res; ###5区间划分解码问题leetcode91 o(n) public static int solve(String s){ int n = s.length(); //首先全0不行,0开头不行,30及以上结尾的不行 if(s.charAt(0)-'0'==0){return 0;} if(n==1&&s.charAt(0)-'0'!=0){ return 1;} int[] f =new int[n]; if(s.charAt(n-1)-'0'==0&&s.charAt(n-2)-'0'>2){return 0;} f[0]=1; if(s.charAt(0) - '0'>2&&s.charAt(1) - '0'==0){ return 0; }else if(s.charAt(0)-'0'>2||s.charAt(1)-'0' ==0||s.charAt(0) - '0'==2&&s.charAt(1) - '0'>6) { f[1]=1; }else { f[1] =2; } for (int i = 0; i < n; i++) { if(i+1 return 0; } } for(int i = 2;i //每日小技巧怎么把char转换成int数字,而不要对应的ASCII码 if(s.charAt(i-1) - '0'>2&&s.charAt(i) - '0'==0){ return 0; } if(s.charAt(i-1) - '0'>2||s.charAt(i-1)-'0'==0||s.charAt(i-1) - '0'==2&&s.charAt(i) - '0'>6){ f[i] = f[i-1]; }else if(s.charAt(i)-'0'==0) { f[i] = f[i-2]; }else{ f[i] = f[i-1]+f[i-2]; } } return f[n-1]; } ###6最长上升子序列leetcode300 o(n^2) public int lengthOfLIS(int[] nums) { int n =nums.length; if(n==0){ return 0; } int[] f = new int[n]; f[0] =1; for(int i= 1;i f[i] = 1; for(int j=0;j
if(nums[i]>nums[j]){ f[i] = Math.max(f[j]+1,f[i]); } } } int res =Integer.MIN_VALUE; for (int i = 0; i < n; i++) { res = Math.max(res,f[i]); } return res; } ###7求网格最小路径leetcode64 o(n^2) public static int findpath(int[][] M){ if(M==null){ return 0; } int x = M.length; int y = M[0].length; int[][] f = new int[x][y]; f[0][0] = M[0][0]; for(int i = 1;i f[i][0] = M[i][0]+f[i-1][0]; } for(int j =1;j f[0][j] = f[0][j-1]+M[0][j]; } for (int i = 1; i < x; i++) { for(int j =1;j f[i][j] = Math.min(f[i-1][j],f[i][j-1])+M[i][j]; } } return f[x-1][y-1]; } ###8给定数字n,求0~n所有数转成2进制中包含1的个数 //10to2:Integer.toBinaryString(int i) //2to10:Integer.valueOf("0101",2) public static void onebits(int N){ int[] f = new int[N+1]; f[0] = 0; f[1] = 1; for(int i =2;i String str = Integer.toBinaryString(i); int n = str.length(); if(str.charAt(n-1)=='0'){ f[i] = f[Integer.valueOf(str.substring(0,n-1),2)]; }else { f[i] = f[Integer.valueOf(str.substring(0,n-1),2)]+1; } } for (int i = 0; i < N+1; i++) { System.out.println(f[i]); } } 优化: public static void onebits(int N){ //10to2:Integer.toBinaryString(int i) //2to10:Integer.valueOf("0101",2) int[] f = new int[N+1]; f[0] = 0; f[1] = 1; for(int i =2;i //右移等于除以二取下整,左移等于乘2 f[i] = f[i>>1] +i&1;//imod2写成位操作的形式会快一点 } for (int i = 0; i < N+1; i++) { System.out.println(f[i]); } } ###求一个数二进制里1的个数 public static int numberOf1(int n){ int count = 0; while(n!=0){ if((n&1) != 0){ ++count; } n = n >> 1; } return count; } ###9给定数组,求面积最大 public static int findmian(int[] height){ int n =height.length; int index_1 = 0; int index_2 = n-1; int res = 0; while(index_1 res = Math.max((index_2-index_1)*Math.min(height[index_1],height[index_2]),res); if(height[index_1] index_1++; }else { index_2--; } } return res; } ###10打家劫舍leetcode198 public static int dajia(int[] nums){ if(nums.length==0||nums==null){ return 0; } int n = nums.length; if(n==1){ return nums[0]; } int[] f= new int[n]; f[0] =nums[0]; f[1] =Math.max(nums[0],nums[1]); for (int i = 2; i < n; i++) { f[i] =Math.max(f[i-2]+nums[i],f[i-1]); } return f[n-1]; } ###11打家劫舍2 leetcode213 public static int dajia2(int[] nums) { if (nums.length == 0 || nums == null) { return 0; } int n = nums.length; if (n == 1) { return nums[0]; } if (n == 2) { return Math.max(nums[0], nums[1]); } if (n == 3) { return Math.max(nums[0],Math.max(nums[1],nums[2])); } int[] f = new int[n]; f[0] = nums[0]; f[1] = Math.max(nums[0], nums[1]); for (int i = 2; i < n - 1; i++) { f[i] = Math.max(f[i - 2] + nums[i], f[i - 1]); } int f1 = f[n - 2]; f[0] = 0; f[1] = nums[1]; for (int i = 2; i < n; i++) { f[i] = Math.max(f[i - 2] + nums[i], f[i - 1]); } int f2 = f[n - 1]; return Math.max(f1, f2); } ###12背包问题leetcode1049 给定一堆数组,要凑出最接近sum的最大值,weight维度为sum,value维度为数组长度。value与weight数组公用一个。 public static int stone2(int [] stones){ if(stones.length==0||stones==null){ return 0; } if(stones.length ==1){ return stones[0]; } int n = stones.length; int sum =0; for (int stone : stones) sum += stone; int weight = sum/2; int[][] f = new int[n][weight+1]; f[0][0] = 0; for (int i = 0; i < n; i++) { f[i][0] = 0; } for (int i = 0; i if(i>=stones[0]){ f[0][i] = stones[0]; }else { f[0][i] = 0; } } for (int i = 1; i < n; i++) { for(int j =1;j if(j>=stones[i]){ f[i][j] = Math.max(f[i-1][j-stones[i]]+stones[i],f[i-1][j]); }else{ f[i][j] = f[i-1][j]; } } } int res = sum-2*f[n-1][weight]; return res; } 优秀代码 public int lastStoneWeightII(int[] stones) { int n = stones.length; int sum = 0; for (int stone : stones) sum += stone; int capacity = sum / 2; int[][] dp = new int[n + 1][capacity + 1]; // 多加一行方便边界处理 for (int i = 1; i <= n; i++) { // 枚举石头 for (int j = 1; j <= capacity; j++) { // 枚举容量 if (stones[i - 1] > j) { // 当前石头重量大于背包容量放不下 dp[i][j] = dp[i - 1][j]; } else { // 能放下的情况的尝试放和不放两种选择选最优 dp[i][j] = Math.max(dp[i - 1][j], stones[i - 1] + dp[i - 1][j - stones[i - 1]]); } } } return sum - 2 * dp[n][capacity]; } 学到的知识 public static int stone(int[] stones){ Queue } //如何定义优先级队列的形式,其默认从小到大排,且先进先出 public static Comparator public int compare(Integer c1, Integer c2) { return (c2-c1); } }; ###13股票买卖时间leetcode63 public static int gupiao(int[] prices){ int n = prices.length; if(n==0||prices==null||n==1){ return 0; } int[] f =new int[n]; f[0] = 0; if(prices[0]>prices[1]){ f[1] = 0; }else { f[1] = prices[1]-prices[0]; } for (int i = 2; i < n; i++) { f[i] = f[i-1]+prices[i]-prices[i-1]; if(f[i]<0){ f[i] = 0; } } int max= 0; for (int i = 0; i < n; i++) { max = Math.max(max,f[i]); } return max; } ###14股票买卖时间可以多次买卖leetcode122 public static int gupiaonum(int[] prices){ int n = prices.length; if(n==0||prices==null||n==1){ return 0; } int res =0; for (int i = 0; i if(prices[i+1]>prices[i]){ res+=prices[i+1]-prices[i]; } } return res; } ###15股票买卖时间 n^2(竟然nm没过,其他的看不懂了 public static int gupiaolirun(int[] prices,int start,int end){ int a = start; int b =0; int[] price1 =new int[end-start+1]; while(b price1[b++] = prices[a++]; } int n = price1.length; if(n==0||price1==null||n==1){ return 0; } int[] f =new int[n]; f[0] = 0; if(price1[0]>price1[1]){ f[1] = 0; }else { f[1] = price1[1]-price1[0]; } for (int i = 2; i < n; i++) { f[i] = f[i-1]+price1[i]-price1[i-1]; if(f[i]<0){ f[i] = 0; } } int max= 0; for (int i = 0; i < n; i++) { max = Math.max(max,f[i]); } return max; } public static int gupiao(int[] prices){ int n = prices.length; if(n==0||prices==null||n==1){ return 0; } if(n==2){ return Math.max(0,prices[1]-prices[0]); } int res =0; for(int k=1;k int left = gupiaolirun(prices,0,k); int right =gupiaolirun(prices,k,n-1); res =Math.max(res,left+right); } return res; } ###16股票买卖时间4可以通解3、 leetcode188 学到的 ArrayList sum=price1.get(1); 解题 public static int gupiao4(int k,int[] prices) { int n = prices.length; if(n==0||prices==null||n==1||k==0){ return 0; } if(n==2){ return Math.max(0,prices[1]-prices[0]); } //k超过一半就是无限制次数 if(k>n/2){ int res =0; for (int i = 0; i if(prices[i+1]>prices[i]){ res+=prices[i+1]-prices[i]; } } return res; } \\横坐标是阶段数(在0-2k之间) int[][] f = new int[n][2 * k + 1]; for (int i = 0; i < n; i++) { f[i][0] = 0; } for (int i = 0; i < 2 * k+1; i++) { f[0][i] = 0; } for (int i = 1; i < n; i++) { for (int j = 1; j < 2 * k+1; j++) { if ((j & 1) == 1) { f[i][j] = Math.max(f[i - 1][j] + prices[i] - prices[i - 1], f[i - 1][j - 1]); } else { f[i][j] = Math.max(f[i - 1][j], f[i - 1][j - 1] + prices[i] - prices[i - 1]); } } } int res = 0; for (int i = 0; i < n; i++) { for (int j = 0; j < 2 * k+1; j++) { if ((j & 1) == 0){ res = Math.max(res, f[i][j]); } } } return res; } ###17俄罗斯套娃问题leetcode354,建议不要函数调用,dp条件很难改,容易陷入贪心怪局 public int maxEnvelopes(int[][] envelopes) { int n =envelopes.length; if(n==0||envelopes==null){ return 0; } if(n==1){ return 1; } Comparator @Override public int compare(int[] o1, int[] o2) { if(o1[0]==o2[0]){ return o1[1]-o2[1]; }else { return o1[0]-o2[0]; } } }; Arrays.sort(envelopes,comp); int[] dp = new int[n]; dp[0] =1; int res =Integer.MIN_VALUE; for(int i= 1;i dp[i] = 1; for(int j=0;j
if(envelopes[i][1]>envelopes[j][1]&& envelopes[i][0] > envelopes[j][0]){ dp[i] = Math.max(dp[j]+1,dp[i]); } } res = Math.max(res,dp[i]); } return res; } ###18求一个数可以最少写成几个完全平方数的和leetcode279 o(n√n) public static int numSquare(int n){ int[] f =new int[n+1]; f[0] = 0; f[1] = 1; for (int i =2;i<=n;i++){ f[i] =Integer.MAX_VALUE; for (int j = 1; (j*j) <=i; j++) { f[i] = Math.min(f[i],f[i-j*j]+1); } } return f[n]; } ###19求一个字符串可以最少划分为多少个回文子串leetcode132 public int minCut(String s) { int n = s.length(); if(n==0||n==1||s==null){ return 0; } int[] f =new int[n]; f[0] = 0; for (int i = 1; i f[i] =Integer.MAX_VALUE; for (int j = 0; j <=i; j++) { if(huiwenfou(s.substring(j,i+1))){ if(j-1<0){ f[i] = 0; }else { f[i] = Math.min(f[i],f[j-1]+1); } } } } return f[n-1]; } public boolean huiwenfou(String s){ String reverse = new StringBuffer(s).reverse().toString(); if(s.equals(reverse)){ return true; } return false; } ###20最长回文子串leetcode05 public static String longestPalindrome(String s) { if (s == null || s.length() == 0) { return ""; } int n = s.length(); boolean[][] f = new boolean[n][n]; f[0][0] = true; for(int j=1;j for(int i =0;i<=j;i++){ if(i==j){ f[i][j]=true; }else if(i+1==j){ if (s.charAt(i)==s.charAt(j)){ f[i][j] =true; }else { f[i][j] = false; } }else if(i+1<=j-1){ if(f[i+1][j-1]==true&&s.charAt(i)==s.charAt(j)){ f[i][j] = true; }else { f[i][j] = false; } } } } int max =Integer.MIN_VALUE; String aa = ""; for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { if(f[i][j]==true){ max =Math.max(max,j-i+1); if(j-i+1==max){ aa = s.substring(i,j+1); } } } } return aa; } ###21求一个字符串中有多少个回文子串leetcode647 class Solution { public int countSubstrings(String s) { if (s == null || s.length() == 0) { return 0; } int n = s.length(); boolean[][] f = new boolean[n][n]; f[0][0] = true; for(int j=1;j for(int i =0;i<=j;i++){ if(i==j){ f[i][j]=true; }else if(i+1==j){ if (s.charAt(i)==s.charAt(j)){ f[i][j] =true; }else { f[i][j] = false; } }else if(i+1<=j-1){ if(f[i+1][j-1]==true&&s.charAt(i)==s.charAt(j)){ f[i][j] = true; }else { f[i][j] = false; } } } } int num = 0; for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { if(f[i][j]==true){ num++; } } } return num; } } ###22博弈论从一边拿石子可以拿一个两个 --必胜即有一种情况让对方必败 public static boolean boyi(int n){ if(n==0){ return false; } boolean[] f =new boolean[n+1]; f[0] = false; f[1] = true; f[2] = true; for(int i =3;i if(f[i-1]==true&&f[i-2]==true){ f[i] = false; }else { f[i] =true; } } return f[n]; } ###23零钱兑换凑成总金额所需的最少的硬币个数leetcode332 public static int coinChange(int[] coins, int amount){ int n =coins.length; if(amount==0){ return 0; } if(n==0||coins==null||amount==0){ return -1; } Arrays.sort(coins); int[] f =new int[amount+1]; f[0] = 0; for (int i = 1; i < amount+1; i++) { f[i] =0x3f3f3f3f; for (int j = 0; j < n; j++) { if(i>=coins[j]){ f[i] = Math.min(f[i-coins[j]]+1,f[i]); } } } return f[amount] == 0x3f3f3f3f ? -1 : f[amount]; } ###24给定数组,N求不超过N能凑成的最大数量0-1背包1维解法 public static int stonexin(int[]ss,int n){ int m =ss.length; boolean[] f =new boolean[n+1]; f[0] = true; for (int i = 0; i for (int j = 0; j < m; j++) { if(i>=ss[j]){ f[i] = f[i]||f[i-ss[j]]; } } } int max =Integer.MIN_VALUE; for (int i = 0; i < n+1; i++) { if(f[i]==true){ max =Math.max(max,i); } } return max; } 背包问题通解 f【i】[w] =f[i-1][w] or f[i-1][w-A[i]] ###25书硬币2leetcode518 二维写法 class Solution { public int change(int amount, int[] coins) { int n =coins.length; if(amount==0){ return 1; } if(n==0||coins==null){ return 0; } int[][] f =new int[n][amount+1]; for (int i = 0; i < n; i++) { f[i][0] =1; } for (int j = 1; j < amount+1; j++) { for (int i = 0; i < n; i++) { if(i==0){ if(j%coins[i]==0){ f[i][j] =1; } }else { if(j>=coins[i]){ f[i][j] = f[i-1][j]+f[i][j-coins[i]]; }else { f[i][j] = f[i-1][j]; } } } } return f[n-1][amount]; } } 一维写法 public static int coins(int amount,int[] coins){ int n =coins.length; if(amount==0){ return 1; } if(n==0||coins==null){ return 0; } int[] f =new int[amount+1]; f[0] =1; for (int j = 0; j < n; j++) { for (int i = coins[j]; i f[i] += f[i-coins[j]]; } } return f[amount]; } ###26最长回文子序列 public static int huiwenzixulie(String s){ int n = s.length(); if(n==1){ return 1; } int[][] f =new int[n][n]; int max =0; f[0][0] =1; for (int j = 1; j < n; j++) { for (int i = 0; i < j; i++) { f[j][j] = 1; if(s.charAt(i)==s.charAt(j)){ f[i][j] = f[i+1][j-1]+2; }else { f[i][j] = Math.max(f[i+1][j],f[i][j-1]); } max = Math.max(f[i][j],max); } } return max; } ###27博弈论左右拿石子(f数组-从i到j能拿到的最大分数) public static boolean stoneGameII(int[] piles){ int n = piles.length; if(n==0||piles==null){ return true; } int[][] f = new int[n][n]; f[0][0] = piles[0]; int sum = piles[0]; for (int j = 1; j < n; j++) { sum += piles[j]; f[j][j] = piles[j]; for (int i = 0; i < j; i++) { f[i][j] = Math.max(piles[i] - f[i+1][j],piles[j] - f[i][j-1]); } } if(f[0][n-1] > 0){ return true; } return false; } ###28二分法 public static int search(int[] nums, int target) { int n = nums.length; int left = 0; int right = n-1; while(left int mid =left+((right-left)>>1); if(nums[mid] left = mid+1; }else { right = mid; } } return left; } ###35切绳子 public int cuttingRope(int n) { if(n==2)return 1; if(n==3)return 2; int[] f = new int[n+1]; f[0] = 0; f[1] = 1; f[2] = 2; for (int i = 3; i < f.length; i++) { f[i] = i; for (int j = 1; j < i; j++) { f[i] = Math.max((i-j)*f[j],f[i]); } } return f[f.length-1]; } ###二分变种 public boolean search(int[] nums, int target) { int l =0,r=nums.length-1; while(l<=r){ int mid = l+(r-l)/2; if(nums[mid]==target)return true; else if(nums[l] if(nums[l]<=target && target else l=mid+1; } else if(nums[l]>nums[mid]){ if(nums[r]>=target && target>nums[mid])l=mid+1; else r=mid-1; } else l++; } return false; } ###贪心求最小区间个数 public int findMinArrowShots(int[][] points) { if(points.length < 1) return 0; Arrays.sort(points, (Comparator.comparingInt(o -> o[1]))); int count = 1; int axis = points[0][1]; for(int i = 1; i < points.length; ++i) { if(axis < points[i][0]) { count++; axis = points[i][1]; } } return count; }