题目
思路1
第一想法是用贪心算法,我先不管我到底能不能跑到终点,只有油够我去下一站我就跑,不够就不跑,换下一站作为起点跑;那么需要两个循环,时间复杂度应该是O(n^2):
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int remain = 0;
for(int i=0;i<cost.size();i++)
{
remain = gas[i];
int j = i;
int flag = 0;
while(remain - cost[j]>=0)
{
remain = remain - cost[j];
j = (j+1)%cost.size();
remain +=gas[j];
flag=1;
if(j==i)
break;
}
if(flag == 1&&j ==i )
return i;
}
return -1;
}
};
但是这种简单粗暴的做法效率很低:
思路2
在题解看到有人说,思路其实和另外一题:最小子序和的思路一样;那一题我是做过的,也很巧妙:
思路是:
若一个子序的起点为负数,那么这个子序之和必定无法取得最大;同理,若在一个子序列中开头的若干连续数字之和为负数,那么也必定无法取得最大;
从这个理论出发,我们可以认为:一个具有最大和的连续子数组res[n],前k(k<=n)个连续数字之和不小于0;
简单的说就比如:
-2是负数,肯定不能拿它作为开头,去到下一位1
1是正数,尝试着拿它作为序列开头,sum=0+1,去到下一位-3
sum=1-3=-2是负数,那肯定不能拿它作为开头,去到下一位4
…以此类推
代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int sum = nums[0];
int res=sum;
for(int i=1;i<nums.size();i++)
{
if(sum<0)
//要是sum是负数,那么应该换下一个作为序列的开头
sum = nums[i];
else
sum += nums[i];
res = max(sum,res);
}
return res;
}
};
这么看就发现可以用最小子序和的思想了做这道题了;我们可以先将gas和cost做差得到一个数列,这个数列的每一个元素就是车子经过要变动的油耗,这整个数列的和要是大于等于0,那么车子就有可能能跑一个环,具体就要看从哪个位置开始跑;
举例:
-2,+3,-2,-2,+3
也和最小子序和一样,要是开头经过的几个油站为负数,那么肯定跑不过去,那么可以把数列分为两部分,前面的部分和是负数,后面部分的和是正数;找到这个后面部分的起点,就是要的结果;这样一次遍历就可以了,时间复杂度在O(n);
代码:
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
vector<int> minus;
int sum = 0;
for(int i=0;i<gas.size();i++)
{
minus.push_back(gas[i]-cost[i]);
sum = sum + gas[i] - cost[i];
}
if(sum<0)
//要是总和为负数的话,那肯定跑不了一环
return -1;
int tmp = minus[0];
int start = 0;
for(int i=1;i<minus.size();i++)
{
if(tmp<0)
{
tmp = minus[i];
start = i;
}
else
tmp+=minus[i];
}
return start;
}
};