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[n−1]
那么组成i的集合最后一个数是j时就有
f
[
i
]
=
f
[
i
−
j
]
f[i] = f[i-j]
f[i]=f[i−j]
最后把所有可能的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;
}
``
##