有n个汽油铺位在圆圈中。每个铺位与其余铺位隔开一定距离。你选择一些需要1升汽油才能覆盖1公里距离的旅行方式。你不能从每个铺位无限地抽取任何数量的汽油,因为每个铺位只有一些有限的汽油。但是你知道所有铺位中汽油的总和等于要覆盖的距离。
即,让P1,P2,... Pn为圆形排列的n个铺位。 d1是p1和p2之间的距离,d2是p2和p3之间的距离。 dn是pn和p1之间的距离。现在找出可以开始旅行的铺位,这样你的旅行方式永远不会耗尽燃料。
答案 0 :(得分:4)
有一个O(n)算法。
假设v [0] = p1-d1,v [1] = p2-d2,...,v [n-1] = pn-dn。我们需要做的就是找到起点i,使得所有部分和不小于0,即v [i]> = 0,v [i] + v [(i + 1)%n] > = 0,v [i] + v [(i + 1)%n] + v [(i + 2)%n]> = 0,...,v [i] + ... + v [(i + n-1)%n]> = 0。
我们可以通过计算s [0] = v [0]找到这样一个起点,s [1] = v [0] + v [1],s [2] = v [0] + v [1 ] + v [2],...,s [n-1] = v [0] + ... + v [n-1],并获取最小s [k]。然后索引(k + 1)%n是起点。
证明:假设最小元素是s [k]。通过问题描述,必须存在最小s [k] <= 0
因为总和v [0] + v [1] + ... + v [n-1] = 0,我们得到v [k + 1] + v [k + 2] + ... v [n-1] = -s [k]&gt; = 0,并且v [k + 1] + ... v [j] <0是不可能的。 0(k
因为s [k]是最小值,我们还有v [k + 1] + v [k + 2] + ... + v [n-1] + v [0] = -s [k ] + v [0] = -s [k] + s [0]&gt; = 0,-s [k] + v [0] + v [1] = -s [k] + s [1]&gt; = 0,..., - s [k] + v [0] + v [1] + ... + v [k-1] = -s [k] + s [k-1]&gt; = 0所以从(k + 1)开始的所有数据都不小于0. QED。
答案 1 :(得分:2)
让我们选择一个我们知道错误的垃圾算法,看看它为什么是错误的......
...符号
当前点:(当前点的加仑汽油,下一点所需的加仑数) - &gt;剩余气体(加仑)
以更多的数学形式:
P [i] :( g(P [i]),d(P [i + 1])) - > (g(P [i]) - d(P [i + 1]))从i = 1到当前点-1的总和
(现在对于糟糕的算法......)
P1: (2,2) -> 0 (at P2)
P2: (5,3) -> 2 (at P3)
P3: (2,4) -> 0 (at P4)
P4: (2,5) -> -3 (ran out of gas 3 miles short of P5)
为了使它达到P5,我们必须增加三加仑气体到P3时,为了在P3上增加3加仑,我们需要在P1加3加仑:
??? -> +3 (at P1)
P1: (2,2) -> 0+3 = 3 (at P2)
P2: (5,3) -> 2+3 = 5 (at P3)
P3: (2,4) -> 0+3 = 3 (at P4)
P4: (2,5) -> -3 +3 = 0 (made it to P5)
因此,诀窍在于找到最糟糕的部分 - 你没有足够的气体来穿过它们。我们知道我们无法从P4,P3,P2或P1开始。我们必须提前开始某个地方并节省足够的气体以使其通过坏区。
毫无疑问,圈内会有多个不好的部分,这有点复杂,但实际上很容易弄明白如何做到这一点。
可能可以在拉伸后的最差拉伸之后接下来的几个点,但是只有当它们没有改变你的天然气储备时。 (例如,在最差拉伸之后的点给你2加仑的气体,让你到下一点2加仑的距离。)
但是,在某些情况下,最后一部分必须最差部分。那是因为在你开始这个部分之前,你需要尽可能多地节省燃气,而在最差拉伸之后的下一个点可能会给你最后一点所需的气体,这意味着你需要在服用之前穿过它在最糟糕的延伸。虽然可能存在多种解决方案,但问题的简单事实是最后遍历最差部分是始终解决方案。这是一些代码:
class point_ {
int gasGiven_;
int distanceToNextPoint_;
public:
int gasGiven() {return gasGiven_;}
int distanceToNextPoint {return distanceToNextPoint_;}
}
class Circle_ {
public:
numberOfPoints;
point_ *P;
}
在main()中:
int indexWorstSection=0;
int numberPointsWorstSection=0;
int worstSum=0;
int currentSum=0;
int i=0;
int startingPoint =0;
// construct the circle, set *P to malloc of numberOfPoints point_'s, fill in all data
while (i<(Circle.numberOfPoints-1) || currentSum<0)
{
currentSum += Circle.P[i].gasGiven() - Circle.P[i].distanceToNextPoint();
if (currentSum < worstSum) { worstSum = currentSum; indexWorstSection=i-numberPointsWorstSection; startingPoint=i;}
if (currentSum>0) { currentSum=0; }
else { numberPointsWorstSection++; }
if (i==(Circle.numberOfPoints-1)) { i=0; }
else { i++; }
}
if (indexWorstSection<0) indexWorstSection=Circle.numberOfPoints+indexWorstSection;
你不能使它成为for循环的原因是因为最坏的部分可能是,例如,从i =(Circle.numberOfPoints -2)到i = 3。如果currentSum小于零,则必须继续返回数组的开头。
没有尝试过代码,并且在近十年内没有做过任何严肃的编程。对不起,如果它有一些错误。毫无疑问,你必须稍微清理一下。
答案 2 :(得分:2)
这可以解决其他几个答案所做的事情 - 找到当你转圈时由净变化汽油增量创建的“图形”的最小值。根据我们开始的地方,与其他起始位置相比,精确值可以向上或向下均匀地移动,但最小值的索引始终是我们可以从哪里开始并且知道我们永远不会用完汽油的有意义的指示。此实现尝试最小化内存使用并在O(n)中完成。
#include <iostream>
int dist[] = { 3, 10, 2, 4, 6, 9 };
int refill[] = { 3, 4, 6, 3, 7, 11 };
static const int n = sizeof dist / sizeof *dist;
int main()
{
int cum = 0, min = 0, min_index = 0;
for (int i = 0; i < n; ++i)
{
cum += refill[i] - dist[i];
std::cout << cum << ' ';
if (cum <= min)
{
min = cum;
min_index = i;
}
}
std::cout << "\nstart from index " << (min_index + 1) % n << " (0-based)\n";
}
看到它正在运行on ideone.com
答案 3 :(得分:1)
此行程的每一段都对燃料产生净影响,从存储中添加并用于进行旅行。您需要做的就是循环一次,在到达每个点时跟踪您的燃油油位,即使它是负的。跟踪最低燃料水平及其发生的点。如果你从那时开始,你将能够从那里开始,而不会耗尽燃料。这假设你从一个空坦克开始,只从你正在开始的地方获取气体,也可以随时取出所有气体,你将永远不会充满并且必须留下气体。
假设你有5分,P1到P5:
Point Fuel Distance to next point
P1 5 8
P2 3 4
P3 12 7
P4 1 4
P5 7 5
如果你选择P1,那么加载5个燃料,前往P2会给你留下-3燃料。继续你得到这些数字:
Point Fuel Balance (before refueling)
P1 0
P2 -3
P3 -4
P4 1
P5 -2
P1 0
因此,如果你从最低值P3开始,你可以回到它(燃料0开始,5点在P4,2点在P5,4点在P1,1点在P2,0点回到P3)
float [] storedFuel = { 1, 1, 1, 1, 1, 1 };
float [] distance = { 1, 1, 1, 1, 1, 1 };
int n = 6;
int FindMinimumPosition()
{
float fuel = 1;
int position = 0;
float minimumFuel = 1;
int minimumPosition = 0;
while (position < n)
{
fuel += storedFuel[position];
fuel -= distance[position];
position++; // could be n which is past array bounds
if (fuel < minimumFuel) {
minimumFuel = fuel;
minimumPosition = position % n;
}
}
return minimumPosition
}
答案 4 :(得分:1)
这是一种适用于O(n)
时间和O(1)
空间的方法。从任何一个站点开始,将其称为0
站,然后前进直到您的汽油耗尽。如果你没有用完汽油,那就完成了。否则,如果您在电台k
和k+1
之间用完,请在电台k+1
重新开始。如果再次通过0
,请记下,如果在此之后用完,则无法完成。
这样做的原因是因为如果你从车站i
开始并且车站k
和k+1
之间的汽油耗尽,那么你也会在车站{{{}之前耗尽汽油1}}如果您从k+1
和i
之间的任何电台开始。
这是一个算法,给定数组k
(汽油)和P
(距离):
D
答案 5 :(得分:0)
在我的脑海中,这是一个应该有效的算法:
现在的问题是找到铺位的圆形排列(或者更简单地说,是e值),以便没有s值是负的。可以简单地重复移动一个,更新s值,直到找到解决方案。 (现在并不是很明显,总有一个解决方案,但我认为有。如果你没有找到解决方案就转移n次,那么你可以保证没有解决方案。)
这是一个O(n ^ 2)算法 - 不是很好。一个好的启发式(可能是一个精确的解决方案)可能是移位,以便最大幅度的负s值转移到位置n。
答案 6 :(得分:0)
对于每个缺口,找到填补缺口之前的铺位所获得的利润或损失,然后跨越差距。在任意一点计算出一个完整圆的每个点剩余的汽油总量,接受它在某个时刻可能是负的。
现在反复循环转移该解决方案。删除最后一点的信息,这将是第一点。设置一个偏移量,以考虑到你现在开始向后一点开始,并且每个剩余点将更多(或更少)汽油。考虑到偏移,添加新点的信息。如果任何点的汽油最小量加上偏差大于零,则该解决方案是可行的。
您可以使用某种log n数据结构(有序映射,优先级队列...)来跟踪最小值,因此这会将成本降低到n log n。
答案 7 :(得分:0)
这是一个O(n)解决方案
private int findStartingPoint(int[] petrol, int[] dist, int[] mileage){
int curPoint = 0;
boolean done = false;
while(curPoint < petrol.length && !done)
{
int prevPoint = curPoint;
int nextPoint = (curPoint+1) % petrol.length;
int nextSolutionPoint = curPoint + 1;
int totalPetrol = 0;
while(nextPoint != curPoint){
totalPetrol += petrol[prevPoint] - (dist[prevPoint]/mileage[prevPoint]);
if(totalPetrol < 0){
curPoint = nextSolutionPoint;
break;
}
prevPoint = nextPoint;
nextPoint = (nextPoint + 1) % petrol.length;
nextSolutionPoint++;
}
if(curPoint == nextPoint){
return curPoint;
}
}
return -1;
}
}