Leetcode动态规划题目

10 篇文章 0 订阅

Leetcode10 正则表达式匹配

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size(), m = p.size();
        s = ' ' + s, p = ' ' + p;
        vector<vector<bool>> f(n+1,vector<bool>(m+1));
//vector<int> vec(4,2); //将含有4个数据的一维动态数组初始为2
//vector< vector<int> > asd1(row, vector<int>(column, 0)); //初始化row*column二维动态数组,初始化值为0
        f[0][0] = true;//第一个串和第二个串都是空串的时候是匹配的
        for (int i = 0; i <= n; i ++ )//i从0开始意思是如果第一个串是空串也是有可能被第二个串匹配的
            for (int j = 1; j <= m; j ++ ) {
                if (j + 1 <= m && p[j + 1] == '*') continue;
                if (i && p[j] != '*') {
                    f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
                } else if (p[j] == '*') {
                    f[i][j] = f[i][j - 2] || (i && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));
                }
            }

        return f[n][m];
    }
};

Leetcode44 通配符匹配

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/*
当p当前位匹配到“*”时,全部或上也是对的,时间复杂度自然是高了点:dp[i][j] = dp[0][j - 1] || dp[1][j-1] || ... || dp[i][j-1]。而另一诠释则是dp[i][j] = dp[i][j-1] || dp[i-1][j](这也是题解给出的诠释)。即:

dp[i][j] = dp[0][j - 1] || dp[1][j-1] || ... || dp[i][j-1]

或

dp[i][j] = dp[i-1][j] || dp[i][j-1]

仔细对比会发现最右边那一项dp[i][j-1]两个式子都一样。那么,dp[0][j - 1] || dp[1][j-1] || ... || dp[i-1][j-1]是否等价于dp[i-1][j]呢?你会发现很容易证明是等价的:假如p(0, j -1)可以匹配s(0, i - 1)的某个子串s(0, k) (k <= i - 1),而p[j]刚好是"*",所以刚好可以适配配剩下的部分,即p(0, j)可以适配s(0, i - 1),反之亦然。

当p[j]为星号时,“p(0, j -1)可以匹配s(0, i - 1)的某个子串s(0, k)” 与 “p(0, j)可以匹配s(0, i - 1)”互为充要条件。

最有意思的是,把全部或上的思路,其实是很顺势而为的,因为“*”可以匹配0到n个任意字符嘛,所以p的前半部分只要能匹配s的任意一个短串就行(哪怕是空串),这样这个理论形成一个整体。比较巧的是,这个“整体”是可以拆的,拆分办法就是前面所说的拿掉最后一项,剩下的一大堆或居然有个等价的结果、而且它已经被计算出来了。
*/
bool isMatch(char * a, char * b){
    char* s = malloc(sizeof(char)*(strlen(a)+10));
    char* p = malloc(sizeof(char)*(strlen(b)+10));
    s[0] = ' ', p[0] = ' ';
    strcpy(s+1,a);
    strcpy(p+1,b);    
    int n = strlen(s), m = strlen(p);
    bool f[n+1][m+1];
    memset(f,0,sizeof(f));
    f[0][0] = true;
    //f[0][i]只有p的前i个字符都是* f[0][i]才是true
    for(int i = 1; i <= m; i++){
        if(p[i] == '*') f[0][i] = true;
        else break;
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            if(p[j] == '*'){
                f[i][j] = f[i-1][j] || f[i][j-1];
            }
            else if(p[j] == '?' || s[i] == p[j]){
                f[i][j] = f[i-1][j-1];
            }
            // if(p[i] != '*'){
            //     f[i][j] = f[i-1][j-1] && (p[j]=='?' || s[i]==p[j]);
            // }
            // else if(p[j] == '*'){
            // //*匹配空串即为 f[i][j]=f[i][j-1]
            // //*匹配的不是空串那么就是f[i][j]=f[0][j-1]||f[1][j-1]||f[2][j-1]||...||f[i-1][j-1]
            // //注意到f[i-1][j]=f[0][j-1]||...||f[i-2][j-1]    
            // f[i][j] = f[i-1][j] || f[i][j-1];
            // }
        }
    }
    return f[n][m];
}

Leetcode97交错字符串

在这里插入图片描述
在这里插入图片描述

bool isInterleave(char * a, char * b, char * c){
    int n = strlen(a), m = strlen(b);
    if(strlen(c) != n + m) return false;
    char* s1 = malloc(sizeof(char)*(n+10));
    char* s2 = malloc(sizeof(char)*(m+10));
    char* s3 = malloc(sizeof(char)*(m+n+10));
    s1[0] = ' ',s2[0] = ' ', s3[0] = ' ';
    strcpy(s1+1,a);
    strcpy(s2+1,b);
    strcpy(s3+1,c);
    bool f[n+10][m+10];
    memset(f,0,sizeof(f));//f[i][j]表示s1的前i个字符和s2的前j个字符能否交错形成s3的前i+j个字符
    for(int i = 0; i <= n; i++){
        for(int j = 0; j <= m; j++){
            if(i==0 && j==0) f[i][j] = true;
            else{
                if(i && s1[i]==s3[i+j]) f[i][j] = f[i-1][j];
                if(j && s2[j] == s3[i+j]) f[i][j] = f[i][j] || f[i][j-1];
            }
        } 
    }
    return f[n][m];
}

Leetcode583两个字符串的删除操作

在这里插入图片描述
在这里插入图片描述

int minDistance(char * word1, char * word2){
    int n = strlen(word1), m = strlen(word2);
    int f[n+10][m+10]; //f[i][j]表示word1前i个字符和word2前j个字符变成相同的字符串,需要的最小步数
    memset(f,0x3f,sizeof(f));
    for(int i = 0; i <= n; i++) f[i][0] = i; //有一个串是空串的话,另一个字符串得全删除才能变得相同
    for(int j = 0; j <= m; j++) f[0][j] = j; 
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            if(word1[i-1] == word2[j-1]) f[i][j] = f[i-1][j-1];
            else{
                f[i][j] = fmin(f[i-1][j]+1,f[i][j-1]+1);//看是删除word1[i-1]还是删除word2[j-1]
            }
        
        }
    }
    return f[n][m];
}

Leetcode115不同子序列

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

int numDistinct(char * a, char * b){
    int n = strlen(a), m = strlen(b);
    char* s = malloc(sizeof(char)*(n+10));
    char* t = malloc(sizeof(char)*(m+10));
    s[0] = ' ';
    t[0] = ' ';
    strcpy(s+1,a);
    strcpy(t+1,b);
    unsigned long long f[n+1][m+1]; //f[i][j]表示s的前i个字符选出来能匹配t的前j个字符的方案数
    f[0][0] = 1; //两个都是空串可以匹配
    for(int i = 1; i <= m; i++) f[0][i] = 0;
    for(int i = 1; i <= n; i++) f[i][0] = 1; //用s去匹配空串,只有一种匹配方式
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            //不用s[i]来匹配
            f[i][j] = f[i-1][j];
            //用s[i]匹配必须s[i]==t[j]
            if(s[i] == t[j]) f[i][j] += f[i-1][j-1];
        }
    }
    return f[n][m];
}

Leetcode132分割回文串II

在这里插入图片描述
在这里插入图片描述

int minCut(char * str){
    int n = strlen(str);
    char* s = malloc(sizeof(char)*(n+10));
    s[0] = ' ';
    strcpy(s+1,str);
    bool st[n+10][n+10];
    memset(st,0,sizeof(st)); //st[i][j]表示s[i]~s[j]这一段是否是回文串
    //先用动态规划求出来st[i][j],不要用普通求法,那样套个循环成n^4了
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= i; j++){
            if(i-j+1<=2) st[j][i] = s[j]==s[i]; //长度小于2时的情况
            else{
                if(s[j] == s[i] && st[j+1][i-1]) st[j][i] = true;
                else st[j][i] = false;
            }
        }
    }
    int f[n+10]; //f[i]表示把前i个字符划分成回文串的最小数目有多少中
    memset(f,0,sizeof(f));
    f[0] = 0; //空字符串不能被划分为回文串
    for(int i = 1; i <= n; i++){
        f[i] = 10000000;
        for(int j = 1; j <= i; j++){//枚举串的起点
            if(st[j][i]) f[i] = fmin(f[i],f[j-1]+1);
        }
    }
    return f[n]-1;
}

Leetcode139单词拆分

在这里插入图片描述
在这里插入图片描述

/***
函数原型:int strncmp(const char* str1, const char* str2, size_t num)
头  文  件:#include <string.h>
返  回  值:(与strncmp相同)str1 = str2   则返回0,
                   str1 > str2  则返回大于0的值,
                   str1 < str2  则返回小于0的值
***/
bool wordBreak(char * str, char ** wordDict, int wordDictSize){
    int n = strlen(str), m = wordDictSize;
    char* s = malloc(sizeof(char)*(n+10));
    s[0] = ' ';
    strcpy(s+1,str);
    bool f[n+10]; //f[i]表示以s[i]结尾的串能不能被划分成wordict里面的字符串
    memset(f,0,sizeof(f));
    f[0] = true; //空字符串可以被划分为wordict里面的空串
    for(int i = 1; i <= n; i++){
        for(int j = 0; j < m; j++){
            int len = strlen(wordDict[j]);
            int k = i - len;  
            if(k < 0) continue;
            f[i] = f[i] || (f[k] && !strncmp(s+k+1,wordDict[j],len));
        }
    }
    return f[n];
}

Leetcode435无重叠区间

在这里插入图片描述

//先求最长无重叠子序列,最后用总数减去即可
struct Num{
    int start,end;
}nums[100010];
int cmp(const void* a,const void* b)
{
    struct Num* aa = (struct Num*)a;
    struct Num* bb = (struct Num*)b;
    if(aa->start > bb->start) return 1;
    return -1;
}

int eraseOverlapIntervals(int** intervals, int intervalsSize, int* intervalsColSize){
    int n = intervalsSize;
    for(int i = 0; i < n; i++){
        nums[i].start = intervals[i][0];
        nums[i].end = intervals[i][1];
    }
    qsort(nums,n,sizeof(nums[0]),cmp);
    int f[n+10]; //f[i]表示0~i中最长不相交区间长度
    f[0] = 1;
    for(int i = 1; i < n; i++){
        f[i] = 1;
        for(int j = 0; j < i; j++){
            if(nums[j].end <= nums[i].start) f[i] = fmax(f[i],f[j]+1);
        }
    }
    for(int i = 0; i < n; i++) printf("%d ",f[i]);
    return n - f[n-1];
}

leetcode1567

在这里插入图片描述

/***
思路:
开辟两个数组,positive[i]表示以i号元素结尾的前i个元素中乘积为正的连续子数组的最大长度
            negative[i]表示以i号元素结尾的前i个元素中乘积为负的连续子数组的最大长度
            如果nums[0]>0  那么 positive[0] = 1
            如果nums[0]<0  那么 negative[0] = 1
            如果nums[0]=0  那么 positvie[0] = negative[0] = 0
当i>=1时
1. 如果nums[i]>0, 
(1).positive[i] = positive[i-1]+1
(2).如果negative[i-1]==0说明以nums[i-1]为结尾的前i-1个数中,不存在连乘起来小于0的子数列
那么negative[i] = 0
如果negative[i-1]>0,那么negative[i] = negative[i-1]+1

2. 如果nums[i]<0, 
(1). negative[i] = positive[i-1] + 1
(2). 如果negative[i-1] = 0 , 那么 positive[i] = 0
     如果negative[i-1]>0,    那么 positive[i] = negative[i-1]+1

3. 如果nums[i] = 0 , 那么positive[i] = negative[i] = 0
***/
class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        int n = nums.size();
        vector<int> positive(n,0);
        vector<int> negative(n,0);

        if(nums[0] > 0) positive[0] = 1;
        if(nums[0] < 0) negative[0] = 1;
        for(int i=1;i<n;i++){
            if(nums[i]==0){
                positive[i] = 0;
                negative[i] = 0;
            }
            else if(nums[i] > 0){
                positive[i] = positive[i-1] + 1;
                if(negative[i-1]==0) negative[i] = 0;
                else negative[i] = negative[i-1] + 1;
            }
            else if(nums[i] < 0)
            {
                negative[i] = positive[i-1] + 1;
                if(negative[i-1]==0) positive[i] = 0;
                else positive[i] = negative[i-1] + 1;
            }
        }
        int res = -1;
        for(int i=0;i<n;i++){
            res = max(res,positive[i]);
        }

        return res;
    }
};

Leetcode410分割数组的最大值

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int splitArray(int* nums, int numsSize, int k){
    int n = numsSize, m = k;
    int f[n+1][n+1]; //f[i][j]表示将nums的前i个数字分割成了j段所得到的j段中最大和段的和值在所有j段划分中最小
    memset(f,0x3f,sizeof(f)); //由于最终要的最小值,所以需要初始化为一个较大的数
    f[0][0] = 0; //从空中划分出空段,结果是0
    for(int i = 1; i <= n; i++) f[i][0] = 0x3f3f3f3f; //不合法方案设为大值
    for(int i = 1; i <= n; i++) f[0][i] = 0;
    int sums[n+10]; //前缀和,代表前i个nums数组中的和
    memset(sums,0,sizeof(sums));
    for(int i = 1; i <= n; i++) sums[i] = sums[i-1] + nums[i-1];
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= fmin(i,m); j++){
            for(int k = 0; k < i; k++){//k代表前k个数
            //前i个数划分了j段,那么把前k个数划分为j-1段,第k+1个数到第i个数为第j段
                f[i][j] = fmin(f[i][j],fmax(f[k][j-1],sums[i]-sums[k]));
            }
        }
    }
    return f[n][m];
}

Leetcode32最长有效括号匹配

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int longestValidParentheses(char * s){
    int n = strlen(s);
    int f[n+1]; //f[i]表示以s[i]结尾的最长有效括号长度
    memset(f,0,sizeof(f));
    for(int i = 1; i < n; i++){
        if(s[i] == '(') continue; //s[i]='('时 f[i]=0
        if(s[i-1] == '('){//...()形如这种类型的括号
            if(i-2 >= 0) f[i] = f[i-2] + 2;
            else f[i] = 2;  
        }
        else if(s[i-1] == ')'){//形如...))这种类型的括号
            if(i-1-f[i-1]>=0 && s[i-1-f[i-1]] == '('){   //i-1-f[i-1]是能否与s[i]=')'匹配的那个左括号的位置
                f[i] = f[i-1] + 2;
                if(i-1-f[i-1]-1>=0) f[i] += f[i-1-f[i-1]-1];  //i-1-f[i-1]-1就是s[i-1-f[i-1]]='('与s[i]=')'匹配后的下一个位置
            }
        }
    }
    int res = 0;
    for(int i = 0; i < n; i++) res = fmax(res,f[i]);
    return res;

}

300最长上升子序列

在这里插入图片描述

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int>f(nums.size()+1);
        f[0] = 1;
        for(int i=1;i<=nums.size()-1;i++){
            f[i] = 1;
            for(int j=0;j<i;j++){
                if(nums[j] < nums[i]) f[i] = max(f[i],f[j]+1);
            }
        }
        int res = -1;
        for(int i=0;i<nums.size();i++) res = max(res,f[i]);

        return res;
    }
};

1143最长公共子序列

在这里插入图片描述

#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1010;
char a[N],b[N];
int f[N][N];

int main()
{
    int n,m;
    cin >> n >> m;
    cin >> a+1 >> b+1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            f[i][j] = max(f[i-1][j],f[i][j-1]);
            if(a[i]==b[j]) f[i][j] = max(f[i][j],f[i-1][j-1]+1);
        }
    }
    cout << f[n][m] << endl;
    return 0;
}

120 三角形最小路径和

在这里插入图片描述

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int n = triangle.size();
        vector<vector<int>>f(n,vector<int>(n));
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++) f[i][j] = 1e4+10;
        }
        f[0][0] = triangle[0][0];
        for(int i=1;i<n;i++){
            for(int j=0;j<=i;j++){
                if(j==0) f[i][j] = f[i-1][j] + triangle[i][j];
                else f[i][j] = min(f[i-1][j-1],f[i-1][j]) + triangle[i][j];
            }
        }
        int res = 1e5;
        for(int i=0;i<n;i++) res = min(res,f[n-1][i]);
        return res;
    }
};

53最大子序和

在这里插入图片描述

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int n = nums.size();
        vector<int>f(n,0);
        f[0] = nums[0];
        for(int i=1;i<nums.size();i++){
            f[i] = max(nums[i],f[i-1]+nums[i]);
        }
        int res = -1e9;
        for(int i=0;i<n;i++) res = max(res,f[i]);
        return res;

    }
};

152乘积最大子数组

在这里插入图片描述

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n = nums.size();
        //f[i]记录以nums[i]结尾的乘积最大的连续子数组
        //g[i]记录以nums[i]结尾的乘积最小的连续子数组
        vector<int>f(n);
        vector<int>g(n);
        f[0] = g[0] = nums[0];
        for(int i=1;i<n;i++){
            if(nums[i] >= 0){
                f[i] = max(nums[i],f[i-1]*nums[i]);
                g[i] = min(nums[i],g[i-1]*nums[i]);
            }
            else{
                f[i] = max(nums[i],g[i-1]*nums[i]);
                g[i] = min(nums[i],f[i-1]*nums[i]);
            }
        }
        int res = -10000;
        for(int i=0;i<n;i++) res = max(res,f[i]);
        return res;
    }
};

Leetcode312戳气球(区间dp)

在这里插入图片描述
在这里插入图片描述

int maxCoins(int* nums, int numsSize){
    int n = numsSize;
    //先在原数组左右两边都加上一个1,表示-1 和 n 越界的情况
    int num[n+10];
    num[0] = 1; //左边界
    num[n+1] = 1; //有边界
    for(int i = 1; i <= n; i++) num[i] = nums[i-1];

    int f[n+10][n+10]; //表示开区间(i,j)戳一个气球得到的[i,j]这个区间能得到的最大值
    memset(f,0,sizeof(f));

    // 先枚举区间长度
    for(int len = 3; len <= n+2; len++){
        for(int i = 0; i + len - 1 <= n+1; i++) //左端点
        {
            int j = i + len - 1; //右端点
            if(len == 3){
                f[i][j] = num[i]*num[i+1]*num[j];
                continue;
            }
            //枚举(i,j)区间中最后一个戳破的气球是哪一个
            for(int k = i+1; k+1 <= j; k++){
                f[i][j] = fmax(f[i][j],f[i][k] + num[i]*num[k]*num[j] + f[k][j]);
            }
        }
    }
    return f[0][n+1];
}

Leetcode1039多边三角形的最低得分(区间dp)

在这里插入图片描述
在这里插入图片描述

int minScoreTriangulation(int* values, int n){
    int f[n+10][n+10]; //f[i][j]表示[i,j]这段区间的点构成的多边形划分三角形构成的最低得分
    memset(f,0,sizeof(f)); //划分不成三角形值,代价是0
    for(int len = 3; len <= n; len++){ //枚举每次多边形的长度
        for(int i = 0; i + len - 1 < n; i++){
            int j = i + len - 1;
            f[i][j] = 0x3f3f3f3f;
            for(int k = i + 1; k < j; k++){ //以(i,j)为底边,连两条边(i,k) (j,k)又形成一个新三角形
                f[i][j] = fmin(f[i][j],f[i][k] + f[k][j] + values[i]*values[j]*values[k]);
            }
        }
    }
    return f[0][n-1];
}

区间dp模板题:Acwing282石子合并

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#define N 310
int n;
int a[N],sum[N]; //a[i]记录第i个石头的重量,i 从1开始, sum是前缀和
int f[N][N]; //f[i][j]表示[i,j]这个区间合并成一个石头堆使用的最小代价

int main()
{
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
    sum[0] = 0;
    for(int i = 1; i <= n; i++) sum[i] = sum[i-1] + a[i];
    memset(f,0x3f,sizeof(f));
    for(int i = 0; i <= n; i++) f[i][i] = 0;
    
    // 区间dp先枚举长度len, 再枚举左端点i 再枚举右端点j
    for(int len = 2; len <= n; len++){
        for(int i = 1; i + len -1 <= n; i++){
            int j = i + len - 1;
            for(int k = i; k < j; k++) f[i][j] = fmin(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
        }
    }
    printf("%d",f[1][n]);
    return 0;
}

Leetcode376摆动序列

在这里插入图片描述

方法一:DP

在这里插入图片描述

int wiggleMaxLength(int* nums, int numsSize){
    int n = numsSize;
    int up[n+10],down[n+10];
    memset(up,0,sizeof(up)); //up[i]表示0~i中选择,且选出来的最后一个元素是摆动序列的上升沿的摆动序列最大长度
    memset(down,0,sizeof(down)); //down[i]表示从0~i中选择,且选出来的最后一个元素是摆动序列下降沿的摆动序列的最大长度
    up[0] = 1,down[0] = 1; //选nums[0]时,只有一个元素的摆动序列其最后一个元素既可以看作上升沿,也可以看作下降沿
    for(int i = 1; i < n; i++){
        if(nums[i] > nums[i-1]){
            up[i] = down[i-1] + 1;
            down[i] = down[i-1];
        }
        else if(nums[i] < nums[i-1]){
            down[i] = up[i-1] + 1;
            up[i] = up[i-1];
        }
        else{
            up[i] = up[i-1];
            down[i] = down[i-1];
        }
    }
    return fmax(up[n-1],down[n-1]);
}

方法二:贪心

在这里插入图片描述
首先去重是为了防止接下来的写法中,有几个连续的值相同的极值点没算进去,这个题的本质就是求那些极值点

int wiggleMaxLength(int* arr, int n){
    if(n == 1) return 1;
    if(n == 2){
        if(arr[0] == arr[1]) return 1;
        return 2;
    }
    int* nums = malloc(sizeof(int)*(n+10)); //去掉数组中连续的几个相同的数,只保留一个
    int cnt = 0;
    int pos = 0;
    while(pos < n){
        nums[cnt++] = arr[pos];
        int i = pos + 1;
        while(i < n && arr[i] == arr[pos]) i++;
        pos = i;
    }
    if(cnt == 1) return 1; //只有 0 0 0这种数组去重后变成[0]
    for(int i = 0; i < cnt; i++) printf("%d ",nums[i]);
    int res = 2; //两个端点肯定是在序列中的
    for(int i = 1; i+1< cnt; i++){
        int a = nums[i-1],b = nums[i],c = nums[i+1];
        if((b>a && b>c) || (b<a && b<c)) res++;
    }
    return res;
}

Leetcode487最大连续1个数II

在这里插入图片描述
在这里插入图片描述

int findMaxConsecutiveOnes(int* nums, int numsSize){
    int n = numsSize;
    int f[n+10],g[n+10];
    memset(f,0,sizeof(f)); //f[i]表示以nums[i]结尾且没有把0变成1过的最长1序列
    memset(g,0,sizeof(g)); //g[i]表示以nums[i]结尾且有把0变成1过的最长1序列
    if(nums[0] == 1){
        f[0] = 1;
        g[0] = 1;
    }
    else if(nums[0] == 0){
        f[0] = 0;
        g[0] = 1;
    }
    for(int i = 1; i < n; i++){
        if(nums[i] == 1){
            f[i] = f[i-1]+1;   
            g[i] = g[i-1]+1;
        }
        else{
            f[i] = 0;
            g[i] = f[i-1] + 1;
        }
    }
    int res = 0;
    for(int i = 0; i < n; i++) res = fmax(res,fmax(f[i],g[i]));
    return res;
}

887鸡蛋掉落

在这里插入图片描述
f(i,j) 表示用j个鸡蛋,最多扔i次,能够得到得到的最大高度是多少
然后 f[i][k] >= n 的最小的那个i就是我们的答案
那么从x楼开始扔鸡蛋就会有两种情况,要么鸡蛋碎了,要么鸡蛋没碎
在这里插入图片描述

#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 110;
int f[N][N];
int n,m;
int main()
{
    while(cin >> n >> m)
    {
        for(int i=1;i<=n;i++) f[i][1] = i;
        for(int i=1;i<=m;i++) f[1][i] = 1;
        
        for(int i=2;i<=n;i++){
            for(int j=2;j<=m;j++){
                f[i][j] = f[i][j-1];
                for(int k=1;k<=i;k++){
                    f[i][j] = min(f[i][j],max(f[k-1][j-1],f[i-k][j])+1);
                }
            }
        }
        cout << f[n][m] << endl;
    }
    return 0;
}

354俄罗斯套娃信封

在这里插入图片描述
先对信封排序,然后变成一个二维的上升子序列问题
f[i] 表示以 a[i] 结尾的上升子序列的最大个数
然后划分是根据倒数第二个元素是什么来进行划分

class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        int n = envelopes.size();
        vector<int>f(n);
        sort(envelopes.begin(),envelopes.end());
      
        for(int i=0;i<n;i++) f[i] = 1;
        for(int i=1;i<n;i++){
            for(int j=0;j<i;j++){
                if(envelopes[j][0]<envelopes[i][0] && envelopes[j][1]<envelopes[i][1]){
                    f[i] = max(f[j] + 1,f[i]);
                }
            }
        }
        int res = 0;
        for(int i=0;i<n;i++) res = max(res,f[i]);
        return res;
    }
};

198打家劫舍

在这里插入图片描述
f[i]表示从 1 ~ i 中选,必选i的方案
g[i]表示从 1 ~ i 中选, 必不选i的方案
f[i] = g[i-1] + w[i]

g[i]是必定不选i号位置,那么就有两种情况,选i-1和不选i-1
g[i] = max(f[i-1],g[i-1])

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        vector<int>f(n+1),g(n+1);
        for(int i=1;i<=n;i++){
            f[i] = g[i-1] + nums[i-1];  //i从1开始的所以nums[i-1]是错开一位来做的
            g[i] = max(g[i-1],f[i-1]);
        }
        return max(f[n],g[n]);
    }
};

213打家劫舍II

在这里插入图片描述
这个题在上一题做法的基础上分为
不选第1个物品,最后结果就是 max(f[n],g[n])
选第一个物品时, 最后结果就是g[n]

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if(nums.size()==0) return 0;
        if(nums.size()==1) return nums[0];
        vector<int>f(n+1);
        vector<int>g(n+1);
        //当不选第一个物品的时候,就相当于从2~n中选,就变成了2~n的这个子问题
        for(int i=2;i<=n;i++){
            f[i] = g[i-1] + nums[i-1];
            g[i] = max(f[i-1],g[i-1]);
        }
        int res = max(f[n],g[n]);
        //当选第1个物品时
        f[1] = nums[0];
        g[1] = INT_MIN;  //g[i]此时是不合法的,需要让g[i]不能更新别的东西
        for(int i=2;i<=n;i++){
            f[i] = g[i-1] + nums[i-1];
            g[i] = max(f[i-1],g[i-1]);
        }
        res = max(res,g[n]);
        return res;
    }
};

Leetcode337打家劫舍III

在这里插入图片描述
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
struct Node{  
    int select;       //选择这个点与子树构成的盗窃总和最大值
    int non_select;   //不选择这个点时与子树构成的盗窃方案的最大值
};

struct Node dfs(struct TreeNode* root){
    if(!root){
        struct Node temp = {0,0};
        return temp;
    }
    struct Node left = dfs(root->left);
    struct Node right = dfs(root->right);
    int select = left.non_select+ right.non_select + root->val;  //选择这个点
    int non_select = fmax(left.select,left.non_select) + fmax(right.select,right.non_select); //不选择这个点
    struct Node res = {select,non_select};
    return res;
}

int rob(struct TreeNode* root){
    if(!root) return 0;
    struct Node res = dfs(root);
    return fmax(res.select,res.non_select);
}

121买卖股票的最佳时机

在这里插入图片描述

class Solution {
public:
//扫描到i号位置时,之前用一个数记录0~i-1中的最小的数,然后计算prices[i]-这个数
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        int res = 0;
        for(int i=0,min_price = INT_MAX;i<n;i++)
        {
            min_price = min(min_price,prices[i]);
            res = max(res,prices[i]-min_price);
        }
        return res;
    }
};

122买卖股票II

在这里插入图片描述
上一题是只能买一只股票,这一题可以买多只股票,但是只有卖出当前持有的股票后才能买入下一个股票
在这里插入图片描述

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<int> a;
        for(int i=0;i+1<prices.size();i++){
            a.push_back(prices[i+1]-prices[i]);
        }
        int res = 0;
        for(int i=0;i<a.size();i++){
            if(a[i] > 0) res += a[i];
        }
        return res;
    }
};

123买卖股票的最佳时机III

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<int> f(n+1);
        vector<int> g(n+1);
        for(int i=1,minp = INT_MAX;i<=n;i++){
            f[i] = max(f[i-1],prices[i-1] - minp);
            minp = min(minp,prices[i-1]);
        }
        for(int i=n,maxp = -1;i>0;i--)
        {
            maxp = max(maxp,prices[i-1]);
            g[i] = maxp - prices[i-1];
        }
        int res = -1;
        for(int i=1;i<=n;i++) res = max(res,f[i-1]+g[i]);
        return res;
    }
};

188买卖股票IV

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        const int inf = -1e9;
        int n = prices.size();
        if(k >= n/2){
            int res = 0;
            for(int i=0;i+1<n;i++){
                if((prices[i+1]-prices[i]) > 0) res += prices[i+1] - prices[i]; 
            }
            return res;
        }
        vector<vector<int>> f(n+1,vector<int>(k+1,-inf));
        vector<vector<int>> g = f;
        f[0][0] = 0;
        int res = 0;
        for(int i=1;i<=n;i++){
            for(int j=0;j<=k;j++){
                f[i][j] = max(f[i-1][j],g[i-1][j]+prices[i-1]);
                g[i][j] = max(g[i-1][j],f[i-1][j-1]-prices[i-1]);
                res = max(res,f[i][j]);
            }
        }

        return res;
    }
};

Leetcode354俄罗斯套娃信封

在这里插入图片描述
在这里插入图片描述
这个题转化为最长上升子序列问题!

struct Num{
    int a,b; //长和宽
}nums[100010];
int cmp(const void* a, const void* b)
{
    struct Num* aa = (struct Num*)a;
    struct Num* bb = (struct Num*)b;
    if(aa->a > bb->a) return 1;
    return -1;
}

int maxEnvelopes(int** envelopes, int envelopesSize, int* envelopesColSize){
    int n = envelopesSize;
    for(int i = 0; i < n; i++){
        nums[i].a = envelopes[i][0];
        nums[i].b = envelopes[i][1];
    }
    qsort(nums,n,sizeof(nums[0]),cmp); //先按一个维度从小到大排序
    int f[n+10];
    memset(f,0,sizeof(f)); //f[i]表示前i个信封可以形成最大长度多少的套娃信封
    f[0] = 1;
    for(int i = 1; i < n; i++){
        f[i] = 1;
        for(int j = 0; j < i; j++){
            if(nums[j].a < nums[i].a && nums[j].b < nums[i].b) f[i] = fmax(f[i],f[j]+1);
        }
    }
    int res = 1;
    for(int i = 0; i < n; i++) res = fmax(res,f[i]);
    return res;
}

516最长回文子序列

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int n = s.size();
        vector<vector<int>> f(n,vector<int>(n));
        for(int len = 1;len<=n;len++){
            for(int i = 0; i + len - 1 < n; i++){
                int j = i + len - 1;
                if(len==1) f[i][j] = 1;
                else{
                    if(s[i]==s[j]) f[i][j] = f[i+1][j-1] + 2;
                    else f[i][j] = max(f[i][j],max(f[i+1][j],f[i][j-1]));
                }
            }
        }
        return f[0][n-1];
    }
};

1039多边三角形部分的最低得分

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

312戳气球

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        vector<int> a(n+2,1);
        for(int i=1;i<=n;i++) a[i] = nums[i-1];
        vector<vector<int>> f(n+2,vector<int>(n+2));
        for(int len = 2;len<=n+1;len++){
            for(int i = 0; i + len <= n + 1; i ++){
                int j = i + len;
                for(int k = i+1; k < j; k++){
                    f[i][j] = max(f[i][j],f[i][k] + f[k][j] + a[i]*a[k]*a[j]);
                }
            }
        }
        return f[0][n+1];
        
    }
};

416分割等和子集

在这里插入图片描述

//看作01背包问题,即有没有选出来和为sum(nums)/2的方案
int* f[2100][2100]; //f[i][j]表示从1~i中选,和不超过j的方案数
bool canPartition(int* nums, int numsSize){
    int sum = 0;
    for(int i = 0; i < numsSize; i++) sum += nums[i];
    if(sum%2) return false;
    int f[numsSize+10][sum/2 + 10];
    memset(f,0,sizeof(f));
    int n = numsSize, m = sum/2;
    f[0][0] = 1;

    for(int i = 1; i <= n; i++){
        for(int j = 0; j <= m; j++){
            f[i][j] = f[i-1][j];
            if(j >= nums[i-1]) f[i][j] = f[i-1][j-nums[i-1]] || f[i-1][j];
        }
    }
    if(f[n][m]) return true;
    return false;
}

Leetcode746

在这里插入图片描述

int min(int a,int b)
{
    return a<b?a:b;
}
int minCostClimbingStairs(int* cost, int costSize){
    int n = costSize;
    if(n == 0) return 0;
    if(n == 1) return cost[0];
    if(n == 2) return min(cost[0],cost[1]);
    int f[n+1];   //f[i]表示到i号位置的花费总和
    memset(f,0,sizeof(f));
    f[0] = 0, f[1] = 0;
    for(int i = 2; i <= n; i++ ) f[i] = min(f[i-1]+cost[i-1],f[i-2]+cost[i-2]);
    return f[n];
}

Leetcode62

在这里插入图片描述

int uniquePaths(int n, int m){
    int f[n+10][m+10];
    memset(f,0,sizeof(f));
    for(int i = 0; i <= n; i++) f[i][0] = 1;
    for(int j = 0; j < m ; j++) f[0][j] = 1;
    for(int i = 1; i < n; i++){
        for(int j = 1; j < m; j++) f[i][j] = f[i-1][j] + f[i][j-1];
    }

    return f[n-1][m-1];
}

Leetcode63

在这里插入图片描述

int uniquePathsWithObstacles(int** obstacleGrid, int obstacleGridSize, int* obstacleGridColSize){
    int n = obstacleGridSize, m = obstacleGridColSize[0];
    int f[n+10][m+10];
    memset(f,0,sizeof(f));
    for(int i = 0; i < n; i ++){
        if(obstacleGrid[i][0]) break;
        f[i][0] = 1;
    }
    for(int j = 0; j < m; j++){
        if(obstacleGrid[0][j]) break;
        f[0][j] = 1;
    }
    for(int i = 1; i < n; i++){
        for(int j = 1; j < m; j++){
            if(obstacleGrid[i][j]) f[i][j] = 0;
            else f[i][j] = f[i-1][j] + f[i][j-1];
        }
    }
    return f[n-1][m-1];
}

Leetcode343

在这里插入图片描述

int integerBreak(int n){
    int f[n+10];
    memset(f,0,sizeof(f)); //f[i]表示数i被拆分后的所有方案的最大乘积
    f[0] = 0, f[1] = 0; //这两个没有拆分方案
    for(int i = 2; i <= n; i++){
        int res = -1; //得先记录这个res再进入j的for循环
        for(int j = 1; j < i; j++){//对j进一步拆分,或者不对j再拆分
            res = fmax(res,fmax((i-j)*j,(i-j)*f[j]));
        }
        f[i] = res;
    }
    return f[n];
}

Leetcode96 不同的二叉搜索树

在这里插入图片描述
在这里插入图片描述

int numTrees(int n){
    int f[n+10];
    memset(f,0,sizeof(f));
    f[0] = 1;
    for(int i = 1; i <= n; i++){
        int res = 0;
        for(int k = 0; k < i; k++){
            res += f[k]*f[i-k-1];
        }
        f[i] = fmax(res,f[i]);
    }
    return f[n];
}

Leetcode95 不同的二叉搜索树II

在这里插入图片描述
该题与上一题相比,不仅仅是求出方案数了,而是要记录所有可能的方案结果

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */


/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
struct TreeNode** dfs(int l, int r,int* returnSize)
{
    if(l > r){//此时方案集合只有一种,就是空树
        struct TreeNode** res = malloc(sizeof(struct TreeNode*));
        *returnSize = 1;
        res[0] = NULL;
        return res;
    }
    struct TreeNode** res = malloc(sizeof(struct TreeNode*)*10100);
    *returnSize = 0;
    //遍历每个根节点
    for(int i = l; i <= r; i++){
        int leftSize,rightSize;
        struct TreeNode** leftTree = dfs(l,i-1,&leftSize);
        struct TreeNode** rightTree = dfs(i+1,r,&rightSize);
        for(int j = 0; j < leftSize; j++){
            for(int k = 0; k < rightSize; k++){
                struct TreeNode* root = malloc(sizeof(struct TreeNode));
                root->val = i;
                root->left = leftTree[j];
                root->right = rightTree[k];
                res[*returnSize] = root;
                (*returnSize)++;
            }
        }
    }
    return res;
}

struct TreeNode** generateTrees(int n, int* returnSize){
    return dfs(1,n,returnSize);
}

Leetcode1049

在这里插入图片描述
在这里插入图片描述

int lastStoneWeightII(int* stones, int stonesSize){
    int n = stonesSize;
    int sum = 0;
    for(int i = 0; i < n; i++) sum += stones[i];
    int m = sum/2;
    int f[n+10][m+10];
    memset(f,0,sizeof(f));
    for(int i = 1; i <= n; i++){
        for(int j = 0; j <= m; j++ ){
            f[i][j] = f[i-1][j];
            if(j >= stones[i-1]) f[i][j] = fmax(f[i][j],f[i-1][j-stones[i-1]]+stones[i-1]);
        }
    }
    int a = f[n][m], b = sum - a;
 
    return abs(a-b);
}

Leetcode494

在这里插入图片描述
在这里插入图片描述

int findTargetSumWays(int* nums, int numsSize, int target){
    int n = numsSize,offset = 1000; 
    int f[30][2100];
    //f[i][j]表示从1~i的数中选出来和为j的方案数
    //j的取值为 -1000~+1000所以用一个偏移量offset = 1000
    memset(f,0,sizeof(f));  
    f[0][0+offset] = 1; //选出来值为0的方案数为1
    for(int i = 1; i <= n; i++){
        for(int j = 0; j <= 2*offset; j++){ // 本来 j范围是[-Offset, Offset],加上偏移量之后变为[-Offset + Offset, Offset + Offset], 即[0, 2* Offset]
        //如果nums[i-1]被选作正数, 那么从 1~i-1中的数选出来和因为  j - nums[i-1]
        if(j-nums[i-1]>=0) f[i][j] += f[i-1][j - nums[i-1]];
        //如果nums[i-1]被选作负数, 那么从 1~i-1中的数选出来和因为  j + nums[i-1]
        if(j+nums[i-1]<=2*offset) f[i][j] += f[i-1][j + nums[i-1]];
        }
    }
    return f[n][target + offset];

}

Leetcode474

在这里插入图片描述
在这里插入图片描述

//01背包不做空间优化时
int get0(char* s)
{
    int ans = 0;
    for(int i = 0; i < strlen(s); i++){
        if(s[i] == '0') ans++;
    }
    return ans;
}

int get1(char* s)
{
    int ans = 0;
    for(int i = 0; i < strlen(s); i++){
        if(s[i] == '1') ans++;
    }
    return ans;
}

int findMaxForm(char ** strs, int strsSize, int m, int n){
    int size = strsSize;
    int f[size+10][m+10][n+10]; //f[i][j][k]表示从前i个物品选,0不超过j个,1不超过k个的最大能选的物品数
    memset(f,0,sizeof(f));
    for(int i = 1; i <= size; i++){
        int num0 = get0(strs[i-1]); //0的个数
        int num1 = get1(strs[i-1]); //1的个税
        for(int j = 0; j <= m; j++){
            for(int k = 0; k <= n; k++){
                f[i][j][k] = fmax(f[i][j][k],f[i-1][j][k]); //不能选第i个物品时
                if(j>=num0 && k >= num1){ //能选择第i个物品时
                    f[i][j][k] = fmax(f[i][j][k],f[i-1][j-num0][k-num1]+1);
                }
            }
        }
    }
    return f[size][m][n];
}

//01背包可以做空间优化
int findMaxForm(char ** strs, int strsSize, int m, int n){
    int f[m+10][n+10];
    memset(f,0,sizeof(f));
    for(int x = 0; x < strsSize; x++){
        int a = 0, b = 0;
        for(int i = 0; i < strlen(strs[x]);i++){
            if(strs[x][i] == '0') a++;
            else b++;
        }
        for(int i = m; i >= a; i--){
            for(int j = n; j>= b; j--){
                f[i][j] = fmax(f[i][j],f[i-a][j-b]+1);
            }
        }
    }
    return f[m][n];
}

Leetcode516最长回文子序列

在这里插入图片描述
在这里插入图片描述

int longestPalindromeSubseq(char * s){
    int n = strlen(s);
    int f[n+10][n+10]; //f[i][j]表示[i,j]区间内的最长回文子串
    memset(f,0,sizeof(f));
    for(int i = 0; i < n; i++) f[i][i] = 1;
    for(int len = 2;len <= n; len++){ //枚举可能的回文串长度
        for(int i = 0; i+len-1<=n-1;i++){ //枚举每个字串可能的起点
            int j = i + len - 1;
            if(s[i] == s[j]) f[i][j] = f[i+1][j-1] + 2; 
            else{
                f[i][j] = fmax(f[i+1][j],f[i][j-1]); //那么[i,j]这个区间的回文子串只可能是由[i+1,j]或者[i,j-1]这个区间来得到的
            }
        }
    }
    return f[0][n-1];
}

Leetcode518

在这里插入图片描述

//状态表示:f[i][j]表示所有由前i种硬币凑出来总钱数等于j的方案数
        //状态计算: 把最后一种硬币拿掉:f[i][j] = f[i-1][j-k*coins[i]], 这样的话时间复杂度是O(n^3)
        //优化: f[i][j] = f[i-1][j] + f[i-1][j-coins[i]] + f[i-1][j-2*coins[i]] + ... + ...
        //      f[i][j-coins[i]] =  f[i-1][j-coins[i]] + f[i-1][j-2*coins[i]] + ... + ...
        //      f[i][j] = f[i-1][j] + f[i][j-coins[i]]
        //      f[j] = f[j] + f[j-coins[i]]

int change(int amount, int* coins, int coinsSize){
    int n = coinsSize, m = amount;
    int f[n+10][m+10];
    memset(f,0,sizeof(f));
    f[0][0] = 1;  //记得初始化,表示选出来为0的情况
    for(int i = 1; i <= n; i++){
        for(int j = 0; j <= m; j++){
            f[i][j] = f[i-1][j];
            if(j>=coins[i-1]) f[i][j] += f[i][j-coins[i-1]];
        }
    }
    return f[n][m];
}

Leetcode377

在这里插入图片描述

方法一:借助背包模板去考虑

在这里插入图片描述
在这里插入图片描述

int combinationSum4(int* nums, int numsSize, int target){
    int n = numsSize, m = target;
    long long f[m+10][m+10];
    memset(f,0,sizeof(f));
    f[0][0] = 1;
    long long res = 0;
    for(int i = 1;i <= m;i ++)
    {
        for(int j = 0;j <= m;j ++)
        {
            for(int k = 0;k < n;k ++)
            {
                if(j >= nums[k])
                    f[i][j] = (f[i][j] + f[i - 1][j - nums[k]]) % INT_MAX;
            }
        }
        res = (res + f[i][m]) % INT_MAX;
    }

    return (int)res;

}

方法二:不用背包模板,直接去考虑

f [ i ] f[i] f[i]表示 i i i的所有划分方案的集合数的最大值(比如 i = 4 i=4 i=4,那么 f [ 4 ] f[4] f[4]就是能够组合成4的所有可能的集合数)
i i i能够由若干数组合而来,比如a+b+c+…+ j = i
由于这个组合是考虑排列序列的,所以我们枚举组合成i的这个组合的最后一个数 j j j,这个 j j j可能是 n u m s [ 0 ] , n u m s [ 1 ] , . . . , n u m s [ n − 1 ] nums[0],nums[1],...,nums[n-1] nums[0],nums[1],...,nums[n1]
那么组成i的集合最后一个数是j时就有 f [ i ] = f [ i − j ] f[i] = f[i-j] f[i]=f[ij]
最后把所有可能的j加起来就是f[i]的结果

int combinationSum4(int* nums, int numsSize, int target){
    int n = numsSize, m = target;
    long long f[m+10]; //f[i]表示i这个值能被划分成的集合数
    memset(f,0,sizeof(f));
    f[0] = 1; //0这个数能被划分的集合数只有1个,那就是空集
    for(int i = 1; i <= m; i++){
        for(int j = 0; j < n; j++)
        {
            if(i>=nums[j]) f[i] = (f[i] + f[i-nums[j]])%INT_MAX;
            // if(i>=nums[j]) printf("%d %d\n",i,i-nums[j]);
        }
    }
    return f[m];
}

Leetcode322

在这里插入图片描述

//这个题是求需要的硬币的个数,零钱兑换II是求的方案数
int coinChange(int* coins, int coinsSize, int amount){
    int n = coinsSize, m = amount;
    int f[n+10][m+10];
    memset(f,0,sizeof(f));
    for(int j = 0; j <= m; j++) f[0][j] = 100000; //求最小值时初始化要是一个比较大的数  
    for(int i = 0; i <= n; i++) f[i][0] = 0; //前i个数中组成值为0的硬币数为0个即可
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            f[i][j] = f[i-1][j];
            // f[i][j] = min(f[i][j],f[i-1][j-coins[i-1]]+1,f[i-1][j-2*coins[i-1]]+2,...,...)
            // f[i][j-coins[i-1]] = min(f[i][j-coins[i-1]],f[i-1][j-2*coins[i-1]]+1,...,...)
            // 所以 f[i][j] = min(f[i][j],f[i-1][j-coins[i-1]]+1)
            // for(int k = 0; k * coins[i-1] <= j; k++){
            //     f[i][j] = fmin(f[i][j],f[i-1][j-k*coins[i-1]]+k);
            // }
            if(j - coins[i-1] >= 0) f[i][j] = fmin(f[i][j],f[i][j-coins[i-1]]+1);
        }
    }
    return f[n][m] > amount ? -1:f[n][m];
}

Leetcode 279

在这里插入图片描述

int *nums;
int numSquares(int n){
    nums = malloc(sizeof(int)*(n+10));
    int cnt = 0;
    for(int i = 1; i <= n; i++){
        if(i*i > n) break;
        nums[cnt++] = i * i; 
    }
    int m = cnt;
    int f[m+10][n+10];
    memset(f,0,sizeof(f));
    for(int i = 0; i <= n; i++) f[0][i] = 1000000;
    for(int i = 0; i <= m; i++) f[i][0] = 0;
    
    for(int i = 1; i <= m; i++){
        for(int j = 1; j <= n; j++){
            f[i][j] = f[i-1][j];
            // for(int k = 0; k*nums[i-1] <= j; k++){
            //     f[i][j] = fmin(f[i][j],f[i-1][j-k*nums[i-1]]+k);
            // }
            if(j>=nums[i-1]) f[i][j] = fmin(f[i][j],f[i][j-nums[i-1]]+1);
        }
    }
    return f[m][n];
}

Leetcode139

在这里插入图片描述
在这里插入图片描述

/***
函数原型:int strncmp(const char* str1, const char* str2, size_t num)
头  文  件:#include <string.h>
返  回  值:(与strncmp相同)str1 = str2   则返回0,
                   str1 > str2  则返回大于0的值,
                   str1 < str2  则返回小于0的值
***/
bool wordBreak(char * s, char ** wordDict, int wordDictSize){
    int n = strlen(s);
    bool f[n+10]; //f[i]表示s前i个字母构成的字串能否被拆分成wordDict中的某个串
    memset(f,0,sizeof(f));
    f[0] = true; //初始化设置为true
    for(int i = 1; i <= n; i++){
    /* 枚举到第i个字母时,i之前的dp已经全部算出来了,我们再枚举字典中的单词,根据每个单词的
    长度决定分割点,假设分割点是k,当分割点后的子串能与字典单词匹配且dp[k]为true,则dp[i]为true
    */
        for(int j = 0; j < wordDictSize; j++){
            int len = strlen(wordDict[j]);
            int k = i - len;
            if(k < 0) continue;
            f[i] = f[i] || (f[k] && !strncmp(s + k, wordDict[j], len));
        }
    }
    return f[n];
}

Leetcode300

在这里插入图片描述

int lengthOfLIS(int* nums, int numsSize){
    int f[numsSize + 10];
    memset(f,0,sizeof(f));
    int n = numsSize;
    for(int i = 1; i <= n; i++){
        f[i] = 1;
        for(int j = 1; j < i; j++){
            if(nums[j-1] < nums[i-1]) f[i] = fmax(f[i],f[j]+1);
        }
    }
    int res = -1;
    for(int i = 1; i <= n; i++) res = fmax(res,f[i]);
    return res;
}

Leetcode674

在这里插入图片描述

int findLengthOfLCIS(int* nums, int numsSize){
    int f[10010];
    memset(f,0,sizeof(f));
    int n = numsSize;
    f[0] = 0,f[1] = 1;
    for(int i = 2; i <= n; i++){
        f[i] = 1;
        if(nums[i-2] >= nums[i-1]) continue;
        f[i] = f[i-1] + 1;
    }
    int res = -1;
    for(int i = 1; i <= n; i++) res = fmax(res,f[i]);
    return res;
}

Leetcode718

在这里插入图片描述

int findLength(int* nums1, int nums1Size, int* nums2, int nums2Size){
    int n = nums1Size, m = nums2Size;
    int f[n+10][m+10]; //f[i][j]表示nums1[i-1]结尾和nums2[j-1]结尾的最大字符串长度
    memset(f,0,sizeof(f));
    int  res = -1;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m ; j++){
            if(nums1[i-1] == nums2[j-1]) f[i][j] = f[i-1][j-1] + 1;
            else{
                f[i][j] = 0;
            }
            res = fmax(res,f[i][j]);
        }
    }
    return res;
}

Leetcode1143

在这里插入图片描述
在这里插入图片描述

int longestCommonSubsequence(char * text1, char * text2){
    int n = strlen(text1),m = strlen(text2);
    int f[1100][1100];
    memset(f,0,sizeof(f));
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            f[i][j] = fmax(f[i-1][j],f[i][j-1]);
            if(text1[i-1] == text2[j-1]) f[i][j] = fmax(f[i][j],f[i-1][j-1]+1);
        }
    }
    return f[n][m];
}

Leetcode1035

在这里插入图片描述
在这里插入图片描述

int maxUncrossedLines(int* nums1, int nums1Size, int* nums2, int nums2Size){
    int n = nums1Size, m = nums2Size;
    int f[1100][1100];
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            f[i][j] = fmax(f[i-1][j],f[i][j-1]);
            if(nums1[i-1] == nums2[j-1]) f[i][j] = fmax(f[i][j],f[i-1][j-1]+1);
        }
    }    
    return f[n][m];
}

Leetcode53

在这里插入图片描述
在这里插入图片描述

int maxSubArray(int* nums, int numsSize){
    int n = numsSize;
    int f[100100];
    memset(f,0,sizeof(f));
    for(int i = 1; i <= n; i++){
        f[i] = fmax(f[i-1]+nums[i-1],nums[i-1]);
    }
    int res = -10000;
    for(int i = 1; i <= n; i++) res = fmax(res,f[i]);
    return res;
}

Leetcode392

在这里插入图片描述

//这个题是判断是否是子序列,不是判断是否是子字符串
bool isSubsequence(char * s, char * t){
    int n = strlen(s),m = strlen(t);
    int i = 0, j = 0;
    while(i < n && j < m){
        while(j < m && s[i] != t[j]) j++;
        if(i!= n && j == m) return false; 
        i++,j++;
        
    }
    return i == n;
}

Leetcode115

在这里插入图片描述
在这里插入图片描述

int numDistinct(char * s, char * t){
    unsigned long long n = strlen(s),m = strlen(t);
    int k = fmax(n,m);
    unsigned long long f[k+10][k+10];
    memset(f,0,sizeof(f));
    for(int i = 0; i <= n; i++) f[i][0] = 1;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            f[i][j] = f[i-1][j];
            if(s[i-1] == t[j-1]) f[i][j] += f[i-1][j-1];
        }
    }
    return f[n][m];
}

Leetcode583

在这里插入图片描述
在这里插入图片描述

int minDistance(char * word1, char * word2){
    int n = strlen(word1), m = strlen(word2);
    int f[n+10][m+10];
    memset(f,0,sizeof(f));
    for(int i = 0; i <= n; i++) f[i][0] = i;
    for(int j = 0; j <= m; j++) f[0][j] = j;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            if(word1[i-1] == word2[j-1]) f[i][j] = f[i-1][j-1];
            else{
                f[i][j] = fmin(f[i-1][j],f[i][j-1])+1;
            }
        }
    }
    return f[n][m];
}

Leetcode72

在这里插入图片描述
在这里插入图片描述

int minDistance(char * word1, char * word2){
    int n = strlen(word1), m = strlen(word2);
    int f[n+10][m+10];
    memset(f,0,sizeof(f));
    for(int i = 0; i <= n; i++) f[i][0] = i;
    for(int j = 0; j <= m; j++) f[0][j] = j;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            f[i][j] = fmin(f[i-1][j],f[i][j-1]) + 1;
            if(word1[i-1] == word2[j-1]) f[i][j] = fmin(f[i][j],f[i-1][j-1]);
            else f[i][j] = fmin(f[i][j],f[i-1][j-1]+1);
        }
    }
    return f[n][m];
}

Leetcode647

在这里插入图片描述

//方法一: 暴力枚举
int countSubstrings(char * s){
    int n = strlen(s);
    int res = 0;
    for(int i = 0; i < n; i++){
        int l = i,r = i;
        while(l>=0 && r < n){
            if(s[l] == s[r]) res++;
            else break;
            l--,r++;
        }
        l = i, r = i + 1;
        while(l >= 0 && r < n){
            if(s[l] == s[r]) res++;
            else break;
            l--,r++;
        }
    }
    return res;
}

Leetcode516

在这里插入图片描述
在这里插入图片描述

int longestPalindromeSubseq(char * s){
    int n = strlen(s);
    int f[n+10][n+10];
    memset(f,0,sizeof(f));
    for(int i = 0; i < n; i++) f[i][i] = 1;
    for(int i = n - 1; i >= 0; i--){
        for(int j = i + 1; j < n; j++){
            if(s[i] == s[j]) f[i][j] = f[i+1][j-1] + 2;
            f[i][j] = fmax(f[i][j],fmax(f[i+1][j],f[i][j-1]));
        }
    }
    return f[0][n-1];
}

Leetcode118杨辉三角

在这里插入图片描述

/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */
int** generate(int numRows, int* returnSize, int** returnColumnSizes){
    int n = numRows;
    *returnColumnSizes = malloc(sizeof(int)*(n+10));
    *returnSize = 0;
    int f[n+10][n+10];
    memset(f,0,sizeof(f));
    for(int i = 1; i<= n; i++){
        f[i][1] = 1;
        f[i][i] = 1;
    }
    for(int i = 1; i <= n; i++){
        for(int j = 2; j<= i; j++){
            f[i][j] = f[i-1][j-1] + f[i-1][j];
        }
    }
    int** res;
    res = malloc(sizeof(int*)*(n+10));
    for(int i = 1; i<= n; i++){
        res[*returnSize] = malloc(sizeof(int)*(n+10));
        int cnt = 0;
        for(int j = 1; j <= i; j++){
            res[*returnSize][cnt++] = f[i][j];
        }
        (*returnColumnSizes)[*returnSize] = cnt;
        (*returnSize)++;
    }
    

    return res;
}

Leetcode119杨辉三角II

在这里插入图片描述

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* getRow(int rowIndex, int* returnSize){
    int n = 34;
    int f[n+10][n+10];
    memset(f,0,sizeof(f));
    for(int i = 1; i <= n; i++){
        f[i][1] = 1;
        f[i][i] = 1;
    }
    for(int i = 1; i <= n; i++){
        for(int j = 2; j <= i; j++){
            f[i][j] = f[i-1][j-1] + f[i-1][j];
        }
    }
    int* res = malloc(sizeof(int)*(n+10));
    int t = rowIndex + 1;
    int cnt = 0;
    for(int i = 1; i <= t; i++){
        res[cnt++] = f[t][i];
    }
    *returnSize = cnt;
    return res;
}

Leetcode120三角形最小路径和

在这里插入图片描述

int minimumTotal(int** triangle, int triangleSize, int* triangleColSize){
    int n = triangleSize, m = triangleColSize[n-1];
    int f[n+10][n+10];
    memset(f,0,sizeof(f));
    for(int i = 1; i <= n; i++)
    {
        f[i][1] = f[i-1][1] + triangle[i-1][0];
        f[i][i] = f[i-1][i-1] + triangle[i-1][i-1];
    }
    for(int i = 1; i <= n; i++){
        for(int j = 2; j < i; j++) f[i][j] = fmin(f[i-1][j-1],f[i-1][j]) + triangle[i-1][j-1];
    }
    int res = 1000000;
    for(int i = 1; i <= n; i++) res = fmin(res,f[n][i]);
    return res;
}

Leetcode174地下城游戏

在这里插入图片描述
这个题和求最大路径和还不一样,因为最终结果最大的那个路径,可能中间会经历负数,也就是在中途就挂了,所以不同从(0,0)开始直接求最大路径和,但是我们可以倒过来推,从(n-1,m-1)出发,求出到(0,0)的最小血量

int calculateMinimumHP(int** dungeon, int dungeonSize, int* dungeonColSize){
    int n = dungeonSize, m = *dungeonColSize;
    int f[n+1][m+1];  //f[i][j]表示吃掉dungeon[i][j]这个点的值之前的在(i,j)这个点的最小血量值
    memset(f,0x3f,sizeof(f)); //因为要倒推,所以是求最小的血量,初始值设置为一个较大的数
    for(int i = n - 1; i >= 0; i--){
        for(int j = m - 1; j >= 0; j--){
            if(i == n-1 && j == m-1){
                if(dungeon[i][j] < 0) f[i][j] = 1 + abs(dungeon[i][j]);//若这个点值<0,那么到这个点时体力值最小得是1+abs(dungeon[i][j])
                else f[i][j] = 1; //若这个点值>=0那么到这个点时体力值最小为1即可
            }
            else{
                if(i+1 < n) f[i][j] = fmin(f[i][j],f[i+1][j]-dungeon[i][j]);
                if(j+1 < m) f[i][j] = fmin(f[i][j],f[i][j+1]-dungeon[i][j]);
                f[i][j] = fmax(1,f[i][j]);
            }
        }
    }
    return f[0][0];
}

Leetcode392判断子序列

在这里插入图片描述

bool isSubsequence(char * s, char * t){
    int n = strlen(s), m = strlen(t);
    //判断s是否为t的子序列
    int i = 0, j = 0;
    while( i < n && j < m)
    {
        while(j < m && s[i] != t[j]) j++;
        if(j == m) break;
        i++,j++;
    }
    return i == n ? true: false;
}

Leetcode1646

在这里插入图片描述

/? 这题就是奇数和偶数获得的方式各自不同罢了
int getMaximumGenerated(int n){
    int f[2*n+10];
    memset(f,0,sizeof(f));
    f[1] = 1;
    for(int i = 2; i <= n; i++){
        if(i%2) f[i] =  f[(i-1)/2] + f[(i-1)/2 + 1];
        else f[i] = f[i/2];
    }
    int res = 0;
    for(int i = 1; i <= n; i++) res = fmax(res,f[i]);
    return res;
}

Leetcode LCP 07.传递信息

在这里插入图片描述
题目链接

//方法一: 把这个看做一个有向图,然后搜索第k轮能到的节点是哪个即可
static int res;
int g[20][20];
void dfs(int n,int k,int u,int round)  //u表示节点号码,round代表轮次
{
    if(round == k){
        if(u == n - 1) res++;
        return;
    }
    for(int i = 0; i < n; i++){
        if(g[u][i]){
            dfs(n,k,i,round+1);
        }
    }
}

int numWays(int n, int** relation, int relationSize, int* relationColSize, int k){
    res = 0;
    int row = relationSize, col = *relationColSize;
    memset(g,0,sizeof(g));
    for(int i = 0; i < relationSize; i++){
        int a = relation[i][0],b = relation[i][1];
        g[a][b] = 1;
    }
    dfs(n,k,0,0);
    return res;
}

Leetcode LCS 01

在这里插入图片描述
在这里插入图片描述

//f[i]表示下载i个插件需要的时间
int leastMinutes(int n){
    int f[n+10];
    memset(f,0,sizeof(f));
    f[1] = 1; //下载1个插件至少需要1分钟
    for(int i = 2; i <= n;i++){
        //f[i]可由前一个+1得到或者在生产 (i+1)/2 时的时刻,停产翻倍, (i+1)是因为向下取整
        f[i] = fmin(f[i-1]+1,f[(i+1)/2]+1);
    }
    return f[n];
}

Leetcode42

在这里插入图片描述

int trap(int* height, int heightSize){
    int n = heightSize;
    int left[n+10],right[n+10]; //left代表i号柱子左边最大的柱子高度,right[i]代表i号柱子右边最大柱子的高度
    left[0] = 0, right[n-1] = 0;
    for(int i = 1; i < n; i++) left[i] = fmax(left[i-1],height[i-1]);
    for(int i = n - 2; i >= 0; i--) right[i] = fmax(right[i+1],height[i+1]);
    int res = 0;
    for(int i = 1; i < n - 1; i++) res += fmax(0,fmin(left[i],right[i]) - height[i]);
    return res;
}

Leetcode44通配符匹配

在这里插入图片描述

在这里插入图片描述

Leetcode45跳跃游戏

在这里插入图片描述

int jump(int* nums, int numsSize){
    int n = numsSize;
    int f[n+10];
    memset(f,0x3f,sizeof(f)); //取最小值记得初始化为较大的数
    f[0] = 0;
    for(int i = 1; i < n; i++){
        for(int j = 0; j < i; j++){
            if(j + nums[j] >= i) f[i] = fmin(f[j]+1,f[i]);
        }
    }
    return f[n-1];
}

Leetcode84柱子中最大矩形

在这里插入图片描述
此题的解法:
对于每个柱子 i,找到左边第一个比它小的柱子的位置left[i],
和找到右边第一个比它小的柱子的位置right[i],
(right[i] - left[i] - 1) * heights[i]是当前柱子所能找到的最大的矩形面积

//朴素做法O(n^2)需要进一步优化时采用单调栈来优化left[i],right[i]的求解过程
int largestRectangleArea(int* heights, int heightsSize){
    int n = heightsSize;
    int left[n+10],right[n+10];
    for(int i = 0; i < n; i++) left[i] = -1; //左边没有比这个柱子低的,这个下标标记为-1
    for(int i = 0; i < n; i++) right[i] = n; //柱子右边没有更高的赋值为n
    for(int i = 0; i < n; i++){
        int j = i;
        while(j>=0 && heights[j]>=heights[i]) j--;
        left[i] = j;
        j = i;
        while(j < n && heights[j]>=heights[i]) j++;
        right[i] = j;
    }
    int res = -1;
    for(int i = 0; i < n; i++){
        res = fmax(res,(abs(left[i]-right[i])-1)*heights[i]);
    }
    return res;

}

Leetcode85最大矩形

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

int maximalRectangle(char** matrix, int matrixSize, int* matrixColSize){
    int n = matrixSize, m = *matrixColSize;
    int row[n+10][m+10];
    memset(row,0,sizeof(row)); //row表示第i行中,以(i,j)结尾的这个在第i行中的最大连续的1的长度
    for(int i = 0; i < n; i++){
        for(int j = 0;j < m; j++){
            if(matrix[i][j] == '0') row[i][j] = 0;
            else{
                if(j == 0) row[i][j] = 1;
                else row[i][j] = row[i][j-1] + 1;
            } 
        }
    }
    int res = 0;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            if(row[i][j] == 0) continue;
            int length = row[i][j];
            int ans = row[i][j];
            for(int k = i - 1; k>=0; k--){
                if(row[k][j] == 0) break;
                length = fmin(length,row[k][j]);
                ans = fmax(ans,length * (i - k + 1)); //是 i - k + 1
            }
            res = fmax(res,ans);
        }
    }
    return res;
}

Leetcode221最大正方形

在这里插入图片描述

在这里插入图片描述

Leetcode91解码方法

在这里插入图片描述
在这里插入图片描述

int numDecodings(char * s){
    int n = strlen(s);
    int f[n+10]; //f[i]表示1 ~ i中字符串的译码方式
    memset(f,0,sizeof(f));
    f[0] = 1;
    for(int i = 1; i <= n; i++){
        if(s[i-1] < '0' || s[i-1] > '9') return 0; //不合法字符串
        f[i] = 0;
        if(s[i-1] != '0') f[i] = f[i-1];
        if(i > 1){
            int t = 10*(s[i-2]-'0') + s[i-1] - '0';
            if(t>=10 && t <= 26) f[i] = f[i] + f[i-2];
        }
    }
    return f[n];
}

Leetcode257粉刷房子

在这里插入图片描述

int minCost(int** costs, int costsSize, int* costsColSize){
    int n = costsSize;
    //f[i][j]表示标号为i的房子被粉刷成颜色j所需要的最小花费
    int f[n+10][5];
    memset(f,0,sizeof(f)); 
    for(int i = 0; i < 3; i++) f[0][i] = costs[0][i]; //0号房子最初可能有三种颜色
    for(int i = 1; i < n; i++){
        f[i][0] = fmin(f[i-1][1],f[i-1][2]) + costs[i][0];
        f[i][1] = fmin(f[i-1][0],f[i-1][2]) + costs[i][1];
        f[i][2] = fmin(f[i-1][0],f[i-1][1]) + costs[i][2];
    }
    return fmin(f[n-1][0],fmin(f[n-1][1],f[n-1][2]));
}

Leetcode264丑数

在这里插入图片描述
在这里插入图片描述

int cnt;
int heap[20000000];
int hash[20000000];
void swap(int* a,int* b)
{
    int t = *b;
    *b = *a;
    *a = t;
}

void down(int u)
{
    int t = u;
    if(2*u <= cnt && heap[2*u] < heap[t]) t = 2*u;
    if((2*u+1)<=cnt && heap[2*u+1] < heap[t]) t = 2*u+1;
    if(t != u){
        swap(&heap[t],&heap[u]);
        down(t);
    }
}

int nthUglyNumber(int n){
    memset(heap,0,sizeof(heap));
    memset(hash,0,sizeof(hash));
    cnt = 1;
    heap[1] = 1;
    hash[1] = 1;
    int res = 1;
    while(n--){
        int t = heap[1];
        if(!n) res = t;
        int a = 2*t, b = 3*t, c = 5*t;
        if(!hash[a]){
            hash[a] = 1;
            heap[++cnt] = a;
        }
        if(!hash[b]){
            hash[b] = 1;
            heap[++cnt] = b;
        }
        if(!hash[c]){
            hash[c] = 1;
            heap[++cnt] = c;
        }
        swap(&heap[1],&heap[cnt]);
        cnt--;
        for(int i = cnt/2;i;i--) down(i);
    }
    return res;
}




Leetcode剑指offer14-1 剪绳子

在这里插入图片描述

int cuttingRope(int n){
    int f[n+10]; //f[i]表示以数字i被拆分的最大乘积值
    memset(f,0,sizeof(f));
    f[0] = 0;
    f[1] = 1;
    f[2] = 1;
    f[3] = 2;
    for(int i = 3; i <= n; i++){
        for(int j = 1; j <= i - 1; j++){
            //i拆分成 j 和 i - j  然后, j 继续拆分或者不拆分, 或者 i-j继续拆分或者不拆分
            f[i] = fmax(fmax(f[i],(i-j)*j),f[j]*f[i-j]);
            f[i] = fmax(f[i],fmax((i-j)*f[j],f[i-j]*j));
        }
    }
    return f[n];
}

Leetcode329矩阵最长递增路径

在这里插入图片描述
在这里插入图片描述

int f[210][210];
int dx[4] = {0,0,1,-1},dy[4] = {1,-1,0,0};

int dfs(int** g,int n,int m,int x,int y)
{
    if(f[x][y] != 0) return f[x][y];
    int ans = 1;
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i],ny = y + dy[i];
        if(nx>=0 && nx<n&& ny>=0 && ny<m && g[nx][ny] > g[x][y]) ans = fmax(ans,dfs(g,n,m,nx,ny)+1);
    }
    f[x][y] = ans;
    return ans;
}

int longestIncreasingPath(int** matrix, int matrixSize, int* matrixColSize){
    int n = matrixSize, m = *matrixColSize;
    memset(f,0,sizeof(f));
    int res = 0;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++) res = fmax(res,dfs(matrix,n,m,i,j));
    }
    return res;
}
相似题目: 滑雪

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define N 10010
int f[N][N],g[N][N]; //f[i][j]表示从(i,j)这个点滑出边界的最长路径经过的点数目
int n,m;
int dx[4] = {0,0,1,-1},dy[4] = {1,-1,0,0};

int dfs(int x,int y)
{
    if(x < 0 || x >= n || y < 0 || y >= m) return 0;
    if(f[x][y] != 0) return f[x][y];
    int ans = 1;
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i], ny = y + dy[i];
        if(x >= 0 && x < n && y >= 0 && y < m && g[nx][ny] < g[x][y]) ans = fmax(ans,dfs(nx,ny)+1);
    }
    f[x][y] = ans;
    return ans;
}


int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++) scanf("%d",&g[i][j]);
    } 
    int res = 0;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++) res = fmax(res,dfs(i,j));
    }
    printf("%d\n",res);
    return 0;
}

Leetcode357 统计各位数字都不同的数字个数

在这里插入图片描述
在这里插入图片描述

int countNumbersWithUniqueDigits(int n){
    if(n == 0) return 1;
    n = fmin(n,10);
    int f[n+10]; //f[i]表示i位有多少中选法
    memset(f,0,sizeof(f));
    f[0] = 9; //最高位有9种选法
    for(int i = 1; i < n; i++) f[i] = f[i-1] * (10-i);
    int res = 1; //值0时
    for(int i = 0; i < n; i++) res += f[i];
    return res;
}

Leetcode562最长1线段

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

int longestLine(int** g, int matSize, int* matColSize){
    int n = matSize, m = *matColSize;
    int row[n+10][m+10];
    int col[n+10][m+10];
    int dg[n+10][m+10];
    int rdg[n+10][m+10];
    memset(row,0,sizeof(row));
    memset(col,0,sizeof(col));
    memset(dg,0,sizeof(dg));
    memset(rdg,0,sizeof(rdg));
    int res = 0;
    for(int i = 0; i < n; i++){
        for(int j = 0; j < m; j++){
            if(g[i][j] == 0){
                row[i][j] = col[i][j] = dg[i][j] = rdg[i][j] = 0;
                continue;
            }
            row[i][j] = col[i][j] = dg[i][j] = rdg[i][j] = 1;
            if(j) row[i][j] = fmax(row[i][j],row[i][j-1]+1);
            if(i) col[i][j] = fmax(col[i][j],col[i-1][j]+1);
            if(i && j) dg[i][j] = fmax(dg[i][j],dg[i-1][j-1]+1);
            if(i && j < m - 1) rdg[i][j] = fmax(rdg[i][j],rdg[i-1][j+1]+1);
            int a = fmax(row[i][j],col[i][j]);
            int b = fmax(dg[i][j],rdg[i][j]);
            res = fmax(res,fmax(a,b));
        }
    }
    return res;
}

Leetcode553最优除法

在这里插入图片描述

在这里插入图片描述

//sprintf(res,"%.3lf",a/b);
//sprintf(res,"%s",str);
char * optimalDivision(int* nums, int numsSize){
    char* res = malloc(sizeof(char)*10000);
    if(numsSize == 1){
        sprintf(res,"%d",nums[0]);
        return res;
    }
    if(numsSize == 2){
        sprintf(res,"%d/%d",nums[0],nums[1]);
        return res;
    }
    int a = nums[0];
    int b = nums[1];
    sprintf(res,"%d/(%d",a,b);
    int pos = strlen(res);
    for(int i = 2; i < numsSize;i++){
        sprintf(res+pos,"/%d",nums[i]);
        pos = strlen(res);
    }
    pos = strlen(res);
    sprintf(res+pos,"%s",")");

    return res;
}

Leetcode368最大整除子集

在这里插入图片描述

    /*
        性质:只要选出来的数相邻之间可以整除,则选出来的序列就可以两两整除!----> 整除具有传递性!
        类似最长上升子序列!
        状态表示:f[i]:表示前i个数中以a[i]结尾的整除序列的最大长度
        状态计算:f[i] = f[j] + 1  (j可取 1,2,3...i - 1 )
    */
int cmp(const void* a,const void* b)
{
    return *(int*)a - *(int*)b; 
}

int* largestDivisibleSubset(int* nums, int numsSize, int* returnSize){
    int n = numsSize;
    qsort(nums,n,sizeof(nums[0]),cmp);
    int f[n+10],pre[n+10];
    memset(f,0,sizeof(f));
    for(int i = 0; i < n; i++) pre[i] = -1;
    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]) continue;
            if(f[i] < f[j]+1){
                f[i] = f[j] + 1;
                pre[i] = j;
            }
        }
    }
    int ans = 0,end = -1;
    for(int i = 0; i < n; i++){
        if(f[i] > ans){
            ans = f[i];
            end = i; 
        }
    }
    int* res = malloc(sizeof(int)*(n+10));
    *returnSize = 0;
    for(int i = end; i != -1; i = pre[i]){
        res[*returnSize] = nums[i];
        (*returnSize)++;
    }
    return res;
}

Leetcode898

在这里插入图片描述

bool hash[1100000001];
int subarrayBitwiseORs(int* arr, int arrSize){
    int max = 0,ans = 0;
    for(int i = 0;i < arrSize;i++)
        max |= arr[i];  //算出最大的或值,因为这是个或运算,每次就是把几个位置变成1
    //不要用memset(hash,0,sizeof(hash));这样把hash里所有值赋值为0的时间是n的,会超时
    for(int i = 0;i < arrSize;i++){  //给那些可能用到的hash里的num赋值为0
        int temp = 0;
        for(int j = i;j < arrSize;j++){
            temp |= arr[j];
            hash[temp] = 0;
            if(temp == max) break;
        }
    }
    for(int i = 0;i < arrSize;i++){
        int temp = 0;
        for(int j = i;j < arrSize;j++){
            temp |= arr[j];
            if(!hash[temp])    ans++,hash[temp] = 1;
            if(temp == max){
                if(!hash[max]) ans++;
                hash[max] = 1;
                break;
            }
        }
    }
    return ans;
}

Leetcode887鸡蛋掉落

在这里插入图片描述

相似题目:ACwing1048鸡蛋的硬度

在这里插入图片描述
在这里插入图片描述 楼层m层,有k个蛋,最坏情况下摔多少次能找到,这个界限楼层?

方法一:
f [ i ] [ j ] f[i][j] f[i][j]表示所有 测量区间长度 是 i i i 且有 j j j个鸡蛋的测量方案集合 中最坏情况下最小的测量次数

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define N 110
#define M 20
int f[N][M]; //f[i][j]表示长度为i的区间内,使用j个鸡蛋最小的最坏情况下能确定1~i是否是临界的最大高度的最小次数
int n,m;

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(f,0,sizeof(f));
        for(int i = 1; i <= n; i++) f[i][1] = i; //只有1个鸡蛋,长度为i需要试i次
        for(int i = 1; i <= m; i++) f[1][i] = 1; //高度为1只需要试1次即可
        for(int i = 2; i <= n; i++){
            for(int j = 2; j <= m; j++){
                //不使用第j个鸡蛋时
                f[i][j] = f[i][j-1];
                //使用第j个鸡蛋时
                for(int k = 1; k <= i; k++){//在第k个位置上使用鸡蛋,可鞥碎,可能不碎
                    f[i][j] = fmin(f[i][j],fmax(f[k-1][j-1],f[i-k][j])+1);
                }
            }
        }
        printf("%d\n",f[n][m]);
    }   
    return 0;
}

``
## 





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新城里的旧少年^_^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值