https://leetcode-cn.com/problems/jump-game/
简单贪心
计算当前能到达的最大距离,计算所有满足小于等于最大距离点,是否可以更新最大距离,以此类推到最后一个元素
class Solution {
public:
bool canJump(vector<int>& nums) {
int n=nums.size();
int most=1;
for(int i=1;i<=n;i++){
if(i<=most){
most=max(most,i+nums[i-1]);
if(most>=n) return true;
}
}
return false;
}
};
自己的思路,最近有点被动态规划洗脑了。于是我就倒着动态规划了,时间复杂度是一如既往的大。
倒推,看某个点可以到达的所有点之中是否有可以到达最后一个点的点。从后向前推导到第一个元素。
class Solution {
public:
bool canJump(vector<int>& nums) {
int n=nums.size();
int could[100010]={};
could[n-1]=1;
for(int i=n-2;i>=0;i--){
for(int j=1;j<=nums[i];j++){
int tag=j+i;
if(could[tag]){
could[i]=1;
break;
}
}
}
return could[0]==1;
}
};
LeetCode名字叫‘大力哥’,喝大力有好身体!giao!
https://leetcode-cn.com/problems/jump-game-ii/
class Solution {
public:
int jump(vector<int>& nums){
int n=nums.size();
int step[100010]={};
for(int i=n-2;i>=0;i--){
step[i]=10000;
for(int j=nums[i];j>=1;j--){
if(i+j>=n-1){
step[i]=1;
break;
}
if(step[i+j]!=-1){
step[i]=min(step[i],1+step[i+j]);
}
}
if(step[i]==10000) step[i]=-1;
}
return step[0];
}
};
终究还是动态规划了,等我看下贪心怎么做。
跳跃游戏II
https://leetcode-cn.com/problems/jump-game-ii/
贪心:
一开始没看懂是为什么这样可以的,后来搞明白了,让我来给出数学证明吧:
[a,……,b,……,c,……,d]假设最佳的路线为a->b->c->d,设为最佳路线A:
那么就满足如下的约束条件:
(1)首先,每个A中元素的后继都必然可以到达:
例如:nums[a]+a>=b
(2)其次,每个A中的元素的后继的后继,(假如有的话),必然不可到达。
例如:nums[a]+a< \, c,注意是严格小于。
那么我们设置边界,边界例如为limit,表示某个区间中的元素能到达的最远位置为limit,则区间[a,b]的边界必然是在哪里呢,必然是出于区间[b,c)中,因为必须要包含b,而必然不能包含c,直接按照最大的区间来放缩,那就假设是这个区间,(足够讨论问题)。边界随着区间的顺次枚举不断更新,这个时候我们发现所有出现的边界的特点都满足:
当前区间的边界必然位于下一个的相邻区间中,这样的话,其实边界的个数,等于路线的步骤数目减去1。为了修正这个1。可以手动加上去,也可以设置0为第一个边界,那么每次遇到边界,数目就++,最终得到的结果就是跳跃的步数。
class Solution {
public:
int jump(vector<int>& nums){
int deep=0,n=nums.size();
int mmax=0,ans=0;
for(int i=0;i<n-1;i++){
mmax=max(mmax,nums[i]+i);
if(deep==i){
ans++;
deep=mmax;
}
}
return ans;
}
};
买卖股票,贪心+最长递增序列
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
找到一段最长的递增子序列,用最大值减去最小值,作为这一段的收益,显然可以证明,这样是收益最大的,否则会有中途一个区间的小损失。
例如[1 2 3 4 5 6],1和6收益最大是5,如果分成好几段,就有[1 2][3 4][5 6]可以看到,收益部分就缺少了[2 3][4 5]这些部分,可以得到
结论,只有最大最小相减才是最优解法。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int st=0,end=0;
int n=prices.size();
int ans=0;
for(int i=0;i<n;i++){
st=prices[i];
while(i+1<n){
if(prices[i]<prices[i+1]) i++;
else break;
}
end=prices[i];
ans+=end-st;
}
return ans;
}
};
https://leetcode-cn.com/problems/gas-station/
最近做的顺风顺水了,不过就是复杂度还是高,虽然能过,毕竟都是一些简单题目。
贪心的精髓在于,找到可以忽略的一些中间过程,直接从下一个可能的解的位置可以计算,节省算力。
就像这个加油站题目,只要模拟一下,然后下一次要从不能到达的第一个点开始接着找,因为已经假定第一个开始的点是净收益大于0的,那么去掉第一个点之后,其他的点更加不可能到达,类似放缩的思想在这里。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int sum=0;
int net[10010];//净收益
int n=gas.size();
for(int i=0;i<n;i++)
net[i]=gas[i]-cost[i];
for(int i=0;i<n;i++){
if(net[i]>=0){
int j=i;
int sum=net[i];
int k=n;
while(--k){
int tag=(++j)%n;
sum+=net[tag];
if(sum<0) break;
}
if(k==0) return i;
else i=j;
}
}
return -1;
}
};
单调栈+贪心
https://leetcode-cn.com/problems/remove-duplicate-letters/
从前往后找的时候,如果这个字母之后还会再出现,就可以按照字典序大胆弹出去,否则就不弹出去。
class Solution {
public:
string removeDuplicateLetters(string s) {
int vis[26]={},num[26]={};//数组要初始化,否则可能乱码,这个错误还犯。。。
for (char ch : s)
num[ch - 'a']++;
string stk;
for (char ch : s) {
if (!vis[ch - 'a']) {
while (!stk.empty() && stk.back() > ch) {
if (num[stk.back() - 'a'] > 0) {
vis[stk.back() - 'a'] = 0;
stk.pop_back();
} else {
break;
}
}
vis[ch - 'a'] = 1;
stk.push_back(ch);
}
num[ch - 'a'] -= 1;
}
return stk;
}
};