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<m+1;i++){
for(int j =1;j<i;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<len;i++){
for(int j=0;j<i;j++){
if(f[j]==true && i-j<bu[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<i;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<x;i++){
f[i][0] = costs[i][0];
}
for(int j =1;j<y;j++){
//看谁先,要是竖着填就是先遍历y,要是横着填就是先遍历x
for(int i =0;i<x;i++){
int res=Integer.MAX_VALUE;
//二维情况下,如果还有一个变量需要控制则需要再加入k
for(int k =0;k<x;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<x;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<n&&s.charAt(i)-'0'==0&&s.charAt(i+1)-'0'==0){
return 0;
}
}
for(int i = 2;i<n;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<n;i++){
f[i] = 1;
for(int j=0;j<i;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<x;i++){
f[i][0] = M[i][0]+f[i-1][0];
}
for(int j =1;j<y;j++){
f[0][j] = f[0][j-1]+M[0][j];
}
for (int i = 1; i < x; i++) {
for(int j =1;j<y;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<N+1;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<N+1;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<index_2){
res = Math.max((index_2-index_1)*Math.min(height[index_1],height[index_2]),res);
if(height[index_1]<height[index_2]){
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 <weight+1; 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<weight+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<Integer> stoneweight = new PriorityQueue<Integer>(idComparator);
}
//如何定义优先级队列的形式,其默认从小到大排,且先进先出
public static Comparator<Integer> idComparator=new Comparator<Integer>() {
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 <n-1 ; 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<end-start+1){
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<n-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<Integer> price1 =new ArrayList<Integer>();
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 <n-1 ; 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<int[]>comp = new Comparator<int[]>() {
@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<n;i++){
dp[i] = 1;
for(int j=0;j<i;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 <n ; 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<n;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<n;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<n+1;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 <n+1 ; 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 <amount+1 ; 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<right){
int mid =left+((right-left)>>1);
if(nums[mid]<target){
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]<nums[mid]){
if(nums[l]<=target && target<nums[mid])r=mid-1;
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;
}