博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LeetCode:Gas Station
阅读量:4673 次
发布时间:2019-06-09

本文共 5598 字,大约阅读时间需要 18 分钟。

题目地址:

There are N gas stations along a circular route, where the amount of gas at station i is gas[i].

You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations.

Return the starting gas station's index if you can travel around the circuit once, otherwise return -1.

Note:

The solution is guaranteed to be unique.

题目大意:有n个加油站首尾相连成一个圆,已知每个加油站的油量,以及从第i个加油站到第i+1个加油站需消耗的油量,问:能否开车从某个加油站出发,循环一圈又回到起点,如果可以返回出发的起点(车的邮箱容量是无限的)。

该题实际上可以转化为求最大字段和问题,只不过是求循环数组的最大子段和。

我们把gas[i] - cost[i]保存在数组edge中,然后对数组edge求最大子段和(求最大子段和和时,我们把edge数组看作首尾相连的循环数组),如果edge的所有元素之和>=0,则汽车可以循环一圈,且这个最大子段的起始位置就是汽车的出发位置;若edge之和小于0,则汽车不能循环一圈。我们之所以对edge数组求最大子段和,是因为按照最大子段和问题的特性,保证了汽车从最大子段的起始端出发到最大子段的末尾的过程中汽车油箱内的油量一直是正数,且汽车走完该子段后,车内可以剩余最多的油。

其实:edge数组所有元素之和>=0 是 汽车可以循环一圈的充分必要条件

另外在求循环数组最大子段和时需要注意几点:(1)最大子段的起始端只会循环一次,即起始端不会两次经过起点(2)防止子段出现首尾相连的情况。这两点代码里都有注释

算法时间复杂度O(N),代码如下:

1 class Solution { 2 public: 3     int canCompleteCircuit(vector
&gas, vector
&cost) 4 { 5 // Note: The Solution object is instantiated only once and is reused by each test case. 6 const int n = gas.size(); 7 vector
edge(n); 8 int sum = 0;//sum是edge数组所有元素之和 9 for(int i = 0; i < n; i++)10 {11 edge[i] = gas[i] - cost[i];12 sum += edge[i];13 }14 if(sum < 0)15 return -1;16 int startpos = 0, endpos = 0;17 int maxSub = maxSubSegment(edge, startpos, endpos);18 return startpos;19 }20 21 //求循环数组的最大子段和,并返回最大子段的首尾位置22 int maxSubSegment(vector
&arr, int &startpos, int &endpos)23 {24 int n = arr.size();25 int result = INT_MIN, sum = -1;26 int starting = -1, ending = -1;//当前子段的起始端27 int i = 0, j=0; //j是i对n求模之前的值,即i = j%n28 while(starting < n)29 {30 if(i == starting)//防止子段首尾相接31 break;32 if(sum >= 0)33 {34 sum += arr[i];35 ending = i;36 }37 else38 {39 sum = arr[i];40 starting = j;41 //设置成j是为了控制while循环结束条件,42 //因为子段的起始位置不会过了尾部又循环到数组首部43 ending = i;44 }45 if(result < sum)46 {47 result = sum;48 startpos = starting;49 endpos = ending;50 }51 i = (i+1)%n;52 j ++;53 }54 55 return result;56 }57 };

 


 

关于循环数组最大子段和可以参考我的另一篇博文:,根据这篇博文,本题有以下代码:

1 class Solution { 2 public: 3     int canCompleteCircuit(vector
&gas, vector
&cost) 4 { 5 // Note: The Solution object is instantiated only once and is reused by each test case. 6 const int n = gas.size(); 7 vector
edge(n); 8 int sum = 0;//sum是edge数组所有元素之和 9 for(int i = 0; i < n; i++)10 {11 edge[i] = gas[i] - cost[i];12 sum += edge[i];13 }14 if(sum < 0)15 return -1;16 int startpos = 0, endpos = 0;17 int maxSub = maxSumCycle(edge, startpos, endpos);18 return startpos;19 }20 21 //求循环数组的最大子段和,并返回最大子段的首尾位置22 int maxSumCycle(vector
&vec, int &left, int&right)23 {24 int maxsum = INT_MIN, curMaxSum = 0;25 int minsum = INT_MAX, curMinSum = 0;26 int sum = 0;27 int begin_max = 0, begin_min = 0;28 int minLeft, minRight;29 for(int i = 0; i < vec.size(); i++)30 {31 sum += vec[i];32 if(curMaxSum >= 0)33 {34 curMaxSum += vec[i];35 }36 else37 {38 curMaxSum = vec[i];39 begin_max = i;40 }41 42 if(maxsum < curMaxSum)43 {44 maxsum = curMaxSum;45 left = begin_max;46 right = i;47 }48 ///求和最小的子数组49 if(curMinSum <= 0)50 {51 curMinSum += vec[i];52 }53 else54 {55 curMinSum = vec[i];56 begin_min = i;57 }58 59 if(minsum > curMinSum)60 {61 minsum = curMinSum;62 minLeft = begin_min;63 minRight = i;64 }65 }66 if(maxsum >= sum - minsum)67 return maxsum;68 else69 {70 left = minRight+1;71 right = minLeft-1;72 return sum - minsum;73 }74 }75 };

 


 

网上看到了一种更优雅的实现,它的本质还是最大子段和,代码如下

1 public class Solution { 2     public int canCompleteCircuit(int[] gas, int[] cost) { 3         // Note: The Solution object is instantiated only once and is reused by each test case. 4         int N = gas.length, startIndex = -1; 5         int sum = 0, total = 0; 6         for(int i = 0; i < N; i++){ 7             sum += (gas[i] - cost[i]); 8             total += (gas[i] - cost[i]); 9             if(sum < 0){10                 startIndex = i; 11                 sum = 0;12             }13         }14         return total >= 0 ? startIndex + 1 : -1;15     }16 }

解释:

若 i = k时,sum小于0,表示车无法到达第k个加油站,必须从下一个加油站开始出发,total来判断是否能够循环一圈

 

【版权声明】转载请注明出处:

转载于:https://www.cnblogs.com/TenosDoIt/p/3389924.html

你可能感兴趣的文章
跟我一起写 Makefile
查看>>
C# uri
查看>>
GPS定位 测试
查看>>
前端使用AngularJS的$resource,后端ASP.NET Web API,实现增删改查
查看>>
探索从 MVC 到 MVVM + Flux 架构模式的转变
查看>>
tornado的异步效果
查看>>
*2.3.2_加入env
查看>>
JS中SetTimeOut和SetInterval方法的区别?
查看>>
Protocol Buffers proto语言语法说明
查看>>
Hibernate双向一对一对象关系模型映射
查看>>
正则表达式(下)
查看>>
熟悉常用的HDFS操作
查看>>
Linux自动化运维第十八课
查看>>
web 10
查看>>
jquery--动态篇
查看>>
npm 是干什么的
查看>>
Android开发之蓝牙(Bluetooth)操作(一)--扫描已经配对的蓝牙设备
查看>>
查找路径php.ini文件到底在哪里?
查看>>
传统认知PK网络认知 刚子扯谈烤串认知
查看>>
字节数组java加密与解密
查看>>