目录
- 198.打家劫舍
- 746. 使用最小花费爬楼梯
- 70. 爬楼梯
- 392. 判断子序列
- 53. 最大子序和
- 121. 买卖股票的最佳时机
- 1025. 除数博弈
- 523. 连续的子数组和
- 91. 解码方法
- 1477. 找两个和为目标值且不重叠的子数组
- 5. 最长回文子串
- 63. 不同路径 II
- 213. 打家劫舍 II
- 152. 乘积最大子数组
- 322. 零钱兑换
- 1423. 可获得的最大点数
- 221. 最大正方形
- 494. 目标和
- 1139. 最大的以 1 为边界的正方形
- 139. 单词拆分
- 416. 分割等和子集
- 264. 丑数 II
- 474. 一和零
- 343. 整数拆分
- 931. 下降路径最小和
- 62. 不同路径
- 120. 三角形最小路径和
- 64. 最小路径和
- 1277. 统计全为 1 的正方形子矩阵
- 338. 比特位计数
198.打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 400
class Solution
{
public:
int rob(vector<int>& nums)
{
int n = nums.size();
if (n <= 0) { return 0; }
if (n == 1) { return nums[0]; }
int* dp = new int[n];
dp[0] = nums[0];//dp[i] 表示nums[0...i] 能够偷到的最高金额
dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < n; ++i)
{
//遍历迄今为止的最大值,两种情况取较大:
//1:投这一家,则上一家不偷(dp[i-2] + nums[i])
//2:不偷这家,则值为上一次偷到的最大值
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
}
return dp[n - 1];
}
};
746. 使用最小花费爬楼梯
数组的每个索引作为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 costi。
每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。
您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。
示例 1:
输入: cost = [10, 15, 20]
输出: 15
解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。
示例 2:
输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出: 6
解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。
注意:
cost 的长度将会在 [2, 1000]。
每一个 cost[i] 将会是一个Integer类型,范围为 [0, 999]。
// class Solution {
// public:
// int minCostClimbingStairs(vector<int>& cost) {
// if(cost.size()==2)return min(cost[0],cost[1]);
// int* dp=new int[cost.size()];
// dp[0]=cost[0];
// dp[1]=cost[1];
// for(int i=2;i<cost.size();++i){
// dp[i]=min(dp[i-2],dp[i-1])+cost[i];
// }
// return min(dp[cost.size()-1],dp[cost.size()-2]);
// }
// };
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
if(cost.size()==2)return min(cost[0],cost[1]);
//int* dp=new int[cost.size()];
int c1=cost[0];
int c2=cost[1];
for(int i=2;i<cost.size();++i){
int c3=min(c1,c2)+cost[i];
c1=c2;
c2=c3;
}
return min(c1,c2);
}
};
70. 爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
class Solution
{
public:
int climbStairs(int n)
{
int* dp = new int[n + 1];
if (n <= 1){
dp[0] = 0;
dp[1] = 1;
}
else {
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
}
for (int i = 3; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; }
return dp[n];
}
};
392. 判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例 1:
s = “abc”, t = “ahbgdc”
返回 true.
示例 2:
s = “axc”, t = “ahbgdc”
返回 false.
class Solution
{
public:
bool isSubsequence(string s, string t)
{
if (s.size() == 0) { return true; }
int i = 0, j = 0;
while (i < s.size() && j < t.size())
{
if (s[i] == t[j])
{
++i;
++j;
}
else { ++j; }
}
return i == s.size();
}
};
53. 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
class Solution
{
public:
int maxSubArray(vector<int>& nums)
{
int n = nums.size();
int* dp = new int[n];
if (n <= 0) { return 0; }
dp[0] = nums[0];
for (int i=1;i<n;i++)
{
if (dp[i - 1] > 0) { dp[i] = dp[i - 1] + nums[i]; }
else { dp[i] = nums[i]; }
}
return (*max_element(dp, dp + n));
}
};
121. 买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(!prices.size()||prices.size()==1){return 0;}
int i=1;
while(prices[i]-prices[i-1]<0&&i<prices.size()-1){++i;}
if(i==prices.size()-1){return prices[i]>prices[i-1]?prices[i]-prices[i-1]:0;}
int diff=prices[i]-prices[i-1],record=prices[--i];
i=i+2;
while(i<prices.size()){
if(prices[i]-record>diff){
diff=prices[i]-record;
}
else if(prices[i]-record<0&&i<prices.size()-1){
record=prices[i];
}
++i;
}
return diff;
}
};
1025. 除数博弈
爱丽丝和鲍勃一起玩游戏,他们轮流行动。爱丽丝先手开局。
最初,黑板上有一个数字 N 。在每个玩家的回合,玩家需要执行以下操作:
选出任一 x,满足 0 < x < N 且 N % x == 0 。
用 N - x 替换黑板上的数字 N 。
如果玩家无法执行这些操作,就会输掉游戏。
只有在爱丽丝在游戏中取得胜利时才返回 True,否则返回 False。假设两个玩家都以最佳状态参与游戏。
示例 1:
输入:2
输出:true
解释:爱丽丝选择 1,鲍勃无法进行操作。
示例 2:
输入:3
输出:false
解释:爱丽丝选择 1,鲍勃也选择 1,然后爱丽丝无法进行操作。
提示:
1 <= N <= 1000
class Solution {
public:
bool divisorGame(int N) {
return N%2==0;
}
};
523. 连续的子数组和
给定一个包含 非负数 的数组和一个目标 整数 k,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,且总和为 k 的倍数,即总和为 n*k,其中 n 也是一个整数。
示例 1:
输入:[23,2,4,6,7], k = 6
输出:True
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6。
示例 2:
输入:[23,2,6,4,7], k = 6
输出:True
解释:[23,2,6,4,7]是大小为 5 的子数组,并且和为 42。
说明:
数组的长度不会超过 10,000 。
你可以认为所有数字总和在 32 位有符号整数范围内。
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
int nums_len=nums.size(),sum=0;
if(nums_len<2)return false;
vector<int>dp(nums_len,0);
dp[0]=nums[0];
for(int i=1;i<nums_len;++i)dp[i]=dp[i-1]+nums[i];
for(int i=0;i<nums_len-1;++i){
for(int j=i+1;j<nums_len;++j){
sum=dp[j]-dp[i]+nums[i];
if(k==0){
if(sum==0)return true;
}
else{
if(sum%k==0)return true;
}
}
}
return false;
}
};
91. 解码方法
一条包含字母 A-Z 的消息通过以下方式进行了编码:
‘A’ -> 1
‘B’ -> 2
…
‘Z’ -> 26
给定一个只包含数字的非空字符串,请计算解码方法的总数。
示例 1:
输入: “12”
输出: 2
解释: 它可以解码为 “AB”(1 2)或者 “L”(12)。
示例 2:
输入: “226”
输出: 3
解释: 它可以解码为 “BZ” (2 26), “VF” (22 6), 或者 “BBF” (2 2 6) 。
class Solution {
public:
int numDecodings(string s)
{
if (s[0] == '0') return 0;
int pre = 1, curr = 1;//dp[-1] = dp[0] = 1
for (int i = 1; i < s.size(); i++) {
int tmp = curr;
if (s[i] == '0')
if (s[i - 1] == '1' || s[i - 1] == '2') curr = pre;
else return 0;
else if (s[i - 1] == '1' || (s[i - 1] == '2' && s[i] >= '1' && s[i] <= '6'))
curr = curr + pre;
pre = tmp;
}
return curr;
}
};
1477. 找两个和为目标值且不重叠的子数组
给你一个整数数组 arr 和一个整数值 target 。
请你在 arr 中找 两个互不重叠的子数组 且它们的和都等于 target 。可能会有多种方案,请你返回满足要求的两个子数组长度和的 最小值 。
请返回满足要求的最小长度和,如果无法找到这样的两个子数组,请返回 -1 。
示例 1:
输入:arr = [3,2,2,4,3], target = 3
输出:2
解释:只有两个子数组和为 3 ([3] 和 [3])。它们的长度和为 2 。
示例 2:
输入:arr = [7,3,4,7], target = 7
输出:2
解释:尽管我们有 3 个互不重叠的子数组和为 7 ([7], [3,4] 和 [7]),但我们会选择第一个和第三个子数组,因为它们的长度和 2 是最小值。
示例 3:
输入:arr = [4,3,2,6,2,3,4], target = 6
输出:-1
解释:我们只有一个和为 6 的子数组。
示例 4:
输入:arr = [5,5,4,4,5], target = 3
输出:-1
解释:我们无法找到和为 3 的子数组。
示例 5:
输入:arr = [3,1,1,1,5,1,2,1], target = 3
输出:3
解释:注意子数组 [1,2] 和 [2,1] 不能成为一个方案因为它们重叠了。
提示:
1 <= arr.length <= 10^5
1 <= arr[i] <= 1000
1 <= target <= 10^8
class Solution {
public:
int minSumOfLengths(vector<int>& arr, int target) {
int n = arr.size();
int res = n+1;
unordered_map<int,int> cnt;
vector<int> dp(n,n+1);
cnt[0] = -1;
int sum = 0;
for(int i = 0; i < arr.size(); ++i){
sum += arr[i];
cnt[sum] = i;
if(i > 0) dp[i] = dp[i-1];
if(cnt.count(sum-target)){
int prev = cnt[sum-target];
if(prev >= 0 && dp[prev] <= n){
res = min(i - prev + dp[prev],res);
}
dp[i] = min(dp[i],i - prev);
}
}
if(res > n) return -1;
return res;
}
};
5. 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
class Solution {
public:
string longestPalindrome(string s) {
string res;
for(int i=0;i<s.size();++i){
string s1=palindrome(s,i,i);
string s2=palindrome(s,i,i+1);
res=res.size()>s1.size()?res:s1;
res=res.size()>s2.size()?res:s2;
}
return res;
}
string palindrome(string &s,int l,int r){
while(l>=0&&r<s.size()&&s[l]==s[r]){
--l;
++r;
}
return s.substr(l+1,r-l-1);
}
};
63. 不同路径 II
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
说明:m 和 n 的值均不超过 100。
示例 1:
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
- 向右 -> 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右 -> 向右
class Solution {
public:
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
if((obstacleGrid[0][0]==1)||(obstacleGrid.size()==0)){return 0;}
int m=obstacleGrid.size(),n=obstacleGrid[0].size();
vector<vector<int> >dp(m,vector<int>(n));
dp[0][0]=1;
for(int i=1;i<m;++i){
if(obstacleGrid[i][0]!=1&&obstacleGrid[i-1][0]!=1){dp[i][0]+=dp[i-1][0];}
}
for(int j=1;j<n;++j){
if(obstacleGrid[0][j]!=1&&obstacleGrid[0][j-1]!=1){dp[0][j]+=dp[0][j-1];}
}
for(int i=1;i<m;++i){
for(int j=1;j<n;++j){
if(obstacleGrid[i][j]!=1)
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
};
213. 打家劫舍 II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
class Solution {
public:
int rob(vector<int>& nums) {
int nums_len=nums.size();
if(nums_len==0)return 0;
if(nums_len==1)return nums[0];
vector<int>dp1(nums.begin()+1,nums.end());
vector<int>dp2(nums.begin(),nums.end()-1);
return max(rob2(dp1),rob2(dp2));
}
int rob2(vector<int>& nums){
int curr=0,pre=0;
for(int i=0;i<nums.size();++i){
int temp=curr;
curr=max(curr,pre+nums[i]);
pre=temp;
}
return curr;
}
};
152. 乘积最大子数组
给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
class Solution {
public:
int maxProduct(vector<int>& nums) {
vector<int>max_dp(nums),min_dp(nums);
for(int i=1;i<nums.size();++i){
max_dp[i]=max(max_dp[i-1]*nums[i],max(min_dp[i-1]*nums[i],nums[i]));
min_dp[i]=min(max_dp[i-1]*nums[i],min(min_dp[i-1]*nums[i],nums[i]));
}
return *max_element(max_dp.begin(),max_dp.end());
}
};
322. 零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
说明:
你可以认为每种硬币的数量是无限的。
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int>dp(amount+1,amount+1);
dp[0]=0;
for(int i=0;i<dp.size();++i){
for(int coin:coins){
if(i-coin<0){continue;}
dp[i]=min(dp[i-coin]+1,dp[i]);
}
}
return (dp[amount]==amount+1)?-1:dp[amount];
}
};
1423. 可获得的最大点数
几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。
每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。
你的点数就是你拿到手中的所有卡牌的点数之和。
给你一个整数数组 cardPoints 和整数 k,请你返回可以获得的最大点数。
示例 1:
输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。
示例 2:
输入:cardPoints = [2,2,2], k = 2
输出:4
解释:无论你拿起哪两张卡牌,可获得的点数总是 4 。
示例 3:
输入:cardPoints = [9,7,7,9,7,7,9], k = 7
输出:55
解释:你必须拿起所有卡牌,可以获得的点数为所有卡牌的点数之和。
示例 4:
输入:cardPoints = [1,1000,1], k = 1
输出:1
解释:你无法拿到中间那张卡牌,所以可以获得的最大点数为 1 。
示例 5:
输入:cardPoints = [1,79,80,1,1,1,200,1], k = 3
输出:202
提示:
1 <= cardPoints.length <= 10^5
1 <= cardPoints[i] <= 10^4
1 <= k <= cardPoints.length
class Solution {
public:
int maxScore(vector<int>& cardPoints, int k) {
int cardPoints_num=cardPoints.size();
vector<int>dp1(cardPoints_num+1);
vector<int>dp2(cardPoints_num+1);
for(int i=0;i<cardPoints_num+1;++i){
dp1[i]= i==0?0:dp1[i-1]+cardPoints[i-1];
dp2[i]= i==0?0:dp2[i-1]+cardPoints[cardPoints_num-i];
}
int maxnum=0;
for(int i=0;i<=k;++i){
maxnum=max(maxnum,dp1[i]+dp2[k-i]);
}
return maxnum;
}
};
221. 最大正方形
在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。
示例:
输入:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
输出: 4
class Solution
{
public:
int maximalSquare(vector<vector<char>>& matrix)
{
if (matrix.size() == 0) { return 0; }
int matrix_rows = matrix.size();
int matrix_cols = matrix[0].size();
if (matrix_rows < 1 || matrix_cols < 1) { return 0; }
vector<vector<int>>dp(matrix_rows+1, vector<int>(matrix_cols+1));
dp[0][0] = matrix[0][0];
int maxsplen = 0;
for (int i = 1; i <= matrix_rows; ++i)
{
for (int j = 1; j <= matrix_cols; ++j)
{
if (matrix[i-1][j-1] == '1')
{
dp[i][j] = min(min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1;
maxsplen = max(dp[i][j], maxsplen);
}
}
}
return maxsplen * maxsplen;
}
};
494. 目标和
给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例:
输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
解释:
-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3
一共有5种方法让最终目标和为3。
提示:
数组非空,且长度不会超过 20 。
初始的数组的和不会超过 1000 。
保证返回的最终结果能被 32 位整数存下。
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
int nums_len=nums.size(),Sum=0;
if(nums_len==0)return 0;
for(int i=0;i<nums_len;++i)Sum+=nums[i];
if((S>Sum)||(Sum+S<0)||(S+Sum)&1)return 0;
int sum=(Sum+S)/2;
vector<int>dp(sum+1,0);
dp[0]=1;
for(int i=0;i<nums_len;++i){
for(int j=sum;j>=nums[i];--j){
dp[j]+=dp[j-nums[i]];
}
}
return dp[sum];
}
};
1139. 最大的以 1 为边界的正方形
给你一个由若干 0 和 1 组成的二维网格 grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0。
示例 1:
输入:grid = [[1,1,1],[1,0,1],[1,1,1]]
输出:9
示例 2:
输入:grid = [[1,1,0,0]]
输出:1
提示:
1 <= grid.length <= 100
1 <= grid[0].length <= 100
grid[i][j] 为 0 或 1
class Solution {
public:
int largest1BorderedSquare(vector<vector<int>>& grid) {
int length = grid.size();//矩阵的长
if (length == 0) return 0;
int width = grid[0].size();//矩阵的宽
//用dp[i][j][0]来表示第i行第j列的 左边 连续的1的个数
//用dp[i][j][1]来表示第i行第j列的 上面 连续的1的个数
vector<vector<vector<int>>> dp(length + 1, vector<vector<int>>(width + 1, vector<int>(2, 0)));
int maxLen = 0;
for (int i = 1; i <= length; i++) {
for (int j = 1; j <= width; j++) {
if (grid[i - 1][j - 1] == 1) {
dp[i][j][0] += dp[i][j - 1][0] + 1;
dp[i][j][1] += dp[i - 1][j][1] + 1;
cout<<dp[i][j][0]<<" "<<dp[i][j][1]<<endl;
//尝试以第i行第j列(当前点)为右下角构成正方形
int len = min(dp[i][j][0], dp[i][j][1]);//最大可能长度
while (len > 0) {
//判断这个可能的正方形右上角左侧是否有连续len个1 && 左下角的上方是否有连续len个1
if (dp[i - len + 1][j][0] >= len && dp[i][j - len + 1][1] >= len)
break;
len--;
}
maxLen = max(maxLen, len);
}
}
}
return maxLen * maxLen;
}
};
139. 单词拆分
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
示例 1:
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以被拆分成 “leet code”。
示例 2:
输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
解释: 返回 true 因为 “applepenapple” 可以被拆分成 “apple pen apple”。
注意你可以重复使用字典中的单词。
示例 3:
输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出: false
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
auto wordDictset=unordered_set<string>();
for(auto word:wordDict)wordDictset.insert(word);
vector<bool>dp(s.size()+1);
dp[0]=true;
for(int i=1;i<s.size()+1;++i){
for(int j=0;j<i;++j){
if(dp[j]&&wordDictset.find(s.substr(j,i-j))!=wordDictset.end()){
dp[i]=true;
break;
}
}
}
return dp[s.size()];
}
};
416. 分割等和子集
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
class Solution {
public:
bool canPartition(vector<int>& nums) {
int nums_len=nums.size(),nums_sum=0;
for(int i=0;i<nums_len;++i){nums_sum+=nums[i];}
if(nums_sum&1)return false;
nums_sum=(nums_sum>>1);
vector<int>dp(nums_sum+1,0);
for(int w:nums){
for(int v=nums_sum;v>=w;--v){
if(dp[v]<dp[v-w]+w){
dp[v]=dp[v-w]+w;
}
}
}
return dp[nums_sum]==nums_sum;
}
};
264. 丑数 II
编写一个程序,找出第 n 个丑数。
丑数就是质因数只包含 2, 3, 5 的正整数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
说明:
1 是丑数。
n 不超过1690。
class Solution {
public:
int nthUglyNumber(int n) {
vector<int>dp(n,1);
int a=0,b=0,c=0;
for(int i=1;i<n;++i){
int n2=dp[a]*2,n3=dp[b]*3,n5=dp[c]*5;
dp[i]=min(n2,min(n3,n5));
if(dp[i]==n2)a++;
if(dp[i]==n3)b++;
if(dp[i]==n5)c++;
}
return dp[n-1];
}
};
474. 一和零
在计算机界中,我们总是追求用有限的资源获取最大的收益。
现在,假设你分别支配着 m 个 0 和 n 个 1。另外,还有一个仅包含 0 和 1 字符串的数组。
你的任务是使用给定的 m 个 0 和 n 个 1 ,找到能拼出存在于数组中的字符串的最大数量。每个 0 和 1 至多被使用一次。
注意:
给定 0 和 1 的数量都不会超过 100。
给定字符串数组的长度不会超过 600。
示例 1:
输入: Array = {“10”, “0001”, “111001”, “1”, “0”}, m = 5, n = 3
输出: 4
解释: 总共 4 个字符串可以通过 5 个 0 和 3 个 1 拼出,即 “10”,“0001”,“1”,“0” 。
示例 2:
输入: Array = {“10”, “0”, “1”}, m = 1, n = 1
输出: 2
解释: 你可以拼出 “10”,但之后就没有剩余数字了。更好的选择是拼出 “0” 和 “1” 。
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
int strs_len=strs.size();
vector<vector<int>>dp(m+1,vector<int>(n+1,0));
if(strs_len==0){return 0;}
for(int str=0;str<strs_len;++str){
int count_one=0,count_zero=0;
for(int count=0;count<strs[str].size();++count){
if(strs[str][count]=='0')count_zero+=1;
else count_one+=1;
}
for(int i=m;i>=count_zero;--i){
for(int j=n;j>=count_one;--j){
dp[i][j]=max(dp[i][j],dp[i-count_zero][j-count_one]+1);
}
}
}
return dp[m][n];
}
};
343. 整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
说明: 你可以假设 n 不小于 2 且不大于 58。
class Solution {
public:
int integerBreak(int n) {
vector<int>dp={0,1,1};
for(int i=3;i<n+1;++i){
dp[i%3]=max(max(1*max(dp[(i-1)%3],i-1),2*max(dp[(i-2)%3],i-2)),3*max(dp[(i-3)%3],i-3));
}
return dp[n%3];
}
};
931. 下降路径最小和
给定一个方形整数数组 A,我们想要得到通过 A 的下降路径的最小和。
下降路径可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列。
示例:
输入:[[1,2,3],[4,5,6],[7,8,9]]
输出:12
解释:
可能的下降路径有:
[1,4,7], [1,4,8], [1,5,7], [1,5,8], [1,5,9]
[2,4,7], [2,4,8], [2,5,7], [2,5,8], [2,5,9], [2,6,8], [2,6,9]
[3,5,7], [3,5,8], [3,5,9], [3,6,8], [3,6,9]
和最小的下降路径是 [1,4,7],所以答案是 12。
提示:
1 <= A.length == A[0].length <= 100
-100 <= A[i][j] <= 100
class Solution {
public:
int minFallingPathSum(vector<vector<int>>& A) {
if(A.size()==1){return A[0][0];}
int rows=A.size(),cols=A.size();
vector<vector<int> >dp(rows,vector<int>(cols));
for(int j=0;j<cols;++j)dp[0][j]+=A[0][j];
for(int i=1;i<rows;++i){
for(int j=0;j<cols;++j){
if(j==0)dp[i][j]=min(dp[i-1][j]+A[i][j],dp[i-1][j+1]+A[i][j]);
else if(j==cols-1)dp[i][j]=min(dp[i-1][j]+A[i][j],dp[i-1][j-1]+A[i][j]);
else dp[i][j]=min(dp[i-1][j]+A[i][j],min(dp[i-1][j-1]+A[i][j],dp[i-1][j+1]+A[i][j]));
//cout<<dp[i][j]<<endl;
}
}
return *min_element(dp[rows-1].begin(),dp[rows-1].end());
}
};
62. 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向右 -> 向下
- 向右 -> 向下 -> 向右
- 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
提示:
1 <= m, n <= 100
题目数据保证答案小于等于 2 * 10 ^ 9
class Solution
{
public:
int uniquePaths(int m, int n)
{
if (m <= 0 || n <= 0) { return 0; }
vector<vector<int>> dp(m,vector<int>(n));
for (int i = 0; i < m; i++) { dp[i][0] = 1; }
for (int i = 0; i < n; i++) { dp[0][i] = 1; }
for (int i = 1; i < m; i++)
{
for (int j = 1; j < n; j++)
{
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
};
120. 三角形最小路径和
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
if(triangle.size()==1){return triangle[0][0];}
int rows=triangle.size();
vector<vector<int> >dp(rows,vector<int>(rows));
dp[0][0]=triangle[0][0];
for(int i=1;i<rows;++i){
dp[i][0]=dp[i-1][0]+triangle[i][0];
dp[i][triangle[i].size()-1]=dp[i-1][triangle[i-1].size()-1]+triangle[i][triangle[i].size()-1];
}
for(int i=2;i<rows;++i){
for(int j=1;j<triangle[i].size()-1;++j){
dp[i][j]=min(dp[i-1][j-1]+triangle[i][j],dp[i-1][j]+triangle[i][j]);
}
}
return *min_element(dp[rows-1].begin(),dp[rows-1].end());
}
};
64. 最小路径和
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
class Solution
{
public:
int minPathSum(vector<vector<int>>& grid)
{
int m = grid.size();
int n = grid[0].size();
if (m <= 0 || n <= 0) { return 0; }
vector<vector<int>>dp(m, vector<int>(n));
dp[0][0] = grid[0][0];
for (int i = 1; i < m; i++) { dp[i][0] = dp[i - 1][0] + grid[i][0]; }
for (int j = 1; j < n; j++) { dp[0][j] = dp[0][j - 1] + grid[0][j]; }
for (int i = 1; i < m; i++)
{
for (int j = 1; j < n; j++)
{
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
};
1277. 统计全为 1 的正方形子矩阵
给你一个 m * n 的矩阵,矩阵中的元素不是 0 就是 1,请你统计并返回其中完全由 1 组成的 正方形 子矩阵的个数。
示例 1:
输入:matrix =
[
[0,1,1,1],
[1,1,1,1],
[0,1,1,1]
]
输出:15
解释:
边长为 1 的正方形有 10 个。
边长为 2 的正方形有 4 个。
边长为 3 的正方形有 1 个。
正方形的总数 = 10 + 4 + 1 = 15.
示例 2:
输入:matrix =
[
[1,0,1],
[1,1,0],
[1,1,0]
]
输出:7
解释:
边长为 1 的正方形有 6 个。
边长为 2 的正方形有 1 个。
正方形的总数 = 6 + 1 = 7.
提示:
1 <= arr.length <= 300
1 <= arr[0].length <= 300
0 <= arr[i][j] <= 1
class Solution {
public:
int countSquares(vector<vector<int>>& matrix) {
int rows=matrix.size(),cols=matrix[0].size();
vector<vector<int> >dp(rows,vector<int>(cols));
int result_count=0;
for (int i = 0; i < rows; ++i){
for(int j=0;j<cols;++j){
if(i==0||j==0){
dp[i][j]=matrix[i][j];
}
else if(matrix[i][j]==0){
dp[i][j]=0;
}
else{
dp[i][j]=min(min(dp[i-1][j],dp[i-1][j-1]),dp[i][j-1])+1;
}
result_count+=dp[i][j];
}
}
return result_count;
}
};
338. 比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1]
示例 2:
输入: 5
输出: [0,1,1,2,1,2]
进阶:
给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
要求算法的空间复杂度为O(n)。
你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。
class Solution {
public:
vector<int> countBits(int num) {
if(num==0){return {0};}
vector<int>dp(num+1);
dp[0]=0;
dp[1]=1;
for(int i=2;i<=num;++i){
if(i%2==0){dp[i]=dp[i/2];}
else dp[i]=dp[i-1]+1;
}
return dp;
}
};