力扣134加油站题解个人理解版存档

0.基本理解

从任意第i个加油站开始,如果能够行驶一周,那么对于从i开始往后的每个加油站j,油箱内剩下的油都得大于等于0

        油箱内剩下的油=这个加油站加的油-走下一段路耗的油

        设油箱内有油gasCount

        初始条件:那么当其在第i个加油站出发时,其到达第i+1个加油站(但还没加油)的时候,gasCount=gas[i]-cost[i],如果gasCount>=0,那么顺利抵达第i+1个加油站,能加上油

        终止条件:一旦gasCount<0,就说明不能继续前进了,需要寻找下一个起始加油站(这是该题破局的重点)

        常规小tips:如果j>=gas.size(),取模,j=j%gas.size()

自此,最基本的想法是两次循环,尝试从0~n(n=gas.size(),下同)作为起始点,走完一遍,如果gasCount一直非负,返回当前起点,否则尝试下一个,如果不存在,返回-1。

给出我的超时代码:

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        printf("size:%d",gas.size());// 33/40 超时
        //不能一圈-gas和小于cost和
        //找到起点
        int gasSum=0,flag=0,j,i,count,count1;//count1-用于限制外层最多遍历一遍
        for(j=0,gasSum=0,count1=1;count1<=gas.size()&&!flag;count1++)//考虑每个点出发
        {
            gasSum=0;
            // case 34/40全0超出时间限制 n^2 达咩
            printf("\nnow %d gasSum: ",j);
            for(i=j,count=0;count<gas.size();count++){
                gasSum+=gas[i];//加油
                gasSum-=cost[(i)%gas.size()];//耗油
                printf("%d ",gasSum);
                i=(i+1)%gas.size();//循环
                if(gasSum<0) break;//油不够
            }
            if(gasSum>=0) {
                flag=1;
                break;
            }
            //如果退出 说明当前i不可达,从这个开始找新的
            if(flag!=1) j=i;
        }
        printf("j: %d",j);
        if(flag==1) return j;
        //else if(flag==1&&j==gas.size()) return j-1;//退出循环前做了j++
        else return -1;
    }

  
};

1.超时分析

O(n^2)超时还是挺常见的,我也分析不出为什么超时,但是暴力算法肯定不是比较好的solution,直接看官方题解,让我们进入下一趴。。。

2.官方题解的理解

首先式0和式1的理解和0.基本理解中提到的是一致的,接着官方题解给出的关于【假设1】的描述就有点confusing了,我的个人理解是这样的。我们为了达成0.基本题解中下划线标出的【破局的重点】就希望,可以用上已经算过的/遍历过得某段情况,所以如果在(x,y)这个区间内,我们可以从x出发抵达y,但是抵达不了y+1,是否还有必要考虑以任意z位于(x,y)区间内作为起点能否抵达y+1呢?如果验证出【不需要考虑x~y之间的z作为新起点】是正确的话,下一个新起点的候选位置就是y+1,而随着新一段类似(x,y)的区间的出现,我们又可以飞跃式的到达新的新起点候选位置,而找起点这个过程和计算(x,y)段上是否可抵达,共同构成【一次遍历】。这也就是官方题解说的“最容易想到的”一次遍历的判断方式。

我跟着题解写了一遍草稿,附上了自己的理解。

最后,如果x能够以作为起点(本身就没有油)的身份到达y,那么它一定能够作为中间点的身份(到x的时候至少还有点油)到达y,也就是说(x,y)这一段一定是安全的(可达,所以换起点的时候不用重新算这段,同时利用式1和式2进行的不等式推导也说明了新起点不可能在x与y之间)。这就真正达成了【一次遍历】背后隐含的【可以用上已经算过的/遍历过得某段情况】。

官方题解代码版:

class Solution {
public:

   int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n = gas.size();
        int i = 0;
        while (i < n) {
            int sumOfGas = 0, sumOfCost = 0;
            int cnt = 0;
            while (cnt < n) {
                int j = (i + cnt) % n;
                sumOfGas += gas[j];
                sumOfCost += cost[j];
                if (sumOfCost > sumOfGas) {
                    break;//但是到不能到达之前,都是能到达的,不用重新计算
                }
                cnt++;
            }
            if (cnt == n) {
                return i;
            } else {
                i = i + cnt + 1;
            }
        }
        return -1;
    }
};

3.写在最后

看官方题解评论区的时候看到了一个关于这个思想的大智慧的表达——

美✌🏻还是通透啊。道理确实是这个道理。

如果走到这里走不下去了的话,那就别回头了,从这里跌倒,就从下一步站起来,走过的路也不必再走,经历了就是结束了。别回头,一定能AC。(#^.^#)

祝我们都好运。

「煎和熬都是变得美味的方式。加油也是。」

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值