卡车在加油站周围移动的算法

时间:2010-02-18 07:08:21

标签: algorithm

您有一辆卡车在圆形轨道上移动,加油站围绕圆圈间隔开。每个站都有有限的气体。卡车上的油箱无限大。加油站之间的距离需要一定量的气体穿过。你只能向一个方向移动。

使用什么算法? 你从哪个加油站开始? 你可以一路走回起跑站吗?

11 个答案:

答案 0 :(得分:24)

是O(n)是可能的。绝对不是TSP。

设x i 是站i的可用气体量减去前往下一站所需的气体量。

要求是Σx i ≥0(足以完成整圆的气体)。

考虑S i = x 1 + x 2 + ... + x i

注意S n ≥0。

现在选择最小的(或者甚至是最大的将会更容易编写代码)k使得S k 是最小的并且从它旁边的站开始。

现在对于k < j≤n,我们有坦克中的气体= S j - S k ≥0。

对于1≤j≤k,我们在罐中有气体= x k + 1 + .. + x n + x 1 + x 2 + .. + x j = S n - S k + S j < / sub>≥0。

因此从k + 1开始将确保每个站点有足够的气体到达下一站。

// C++ code. gas[i] is the gas at station i, cost[i] is the cost from station i to (i+1)%n
int circ(vector<int> &gas, vector<int> &cost) {
    int min_S=INT_MAX, S=0, position=0;
    for(int i=0;i<gas.size();i++)
    {
        S += gas[i] - cost[i];
        if(S<min_S)
        {
            min_S = S;
            position = (i+1) % gas.size();
        }
    }
    if(S>=0)
        return position;
    else
        return -1;
}

答案 1 :(得分:11)

这是一种在O(n)时间和O(1)空间中工作的方法(与Aryabhatta的答案相对于O(n)空间)。

从任何一个站点开始,将其称为0站点,然后前进,直到您的燃气耗尽。如果你没有用完汽油,那就完成了。否则,如果在k和k + 1站之间用完,请在站k + 1处重新开始。如果再次传递0,请记下,如果在此之后用完,则无法完成。

这样做的原因是因为如果你从车站i开始并且在车站k和k + 1之间耗尽汽油,那么如果你从i和i之间的任何车站开始,那么你也将在车站k + 1之前耗尽汽油。 ķ。

这是一个算法,给定一个数组P(汽油)和D(距离):

int position = 0;
int petrol = P[0];
int distance = D[0];

int start = 0;
while (start < n) {
    while (petrol >= distance) {
        petrol += P[++position % N] - distance;
        distance = D[position % N];
        if (position % N == start)
            return start;
    }
    start = position;
    petrol = P[start];
}
return -1;

答案 2 :(得分:4)

cost成为下一站费用的数组,gas是我们可以补充多少燃料的数组

我们计算gas[i]cost[i]之间的差异,称为diff,其中i是我们当前的加油站。

如果cost[i] > gas[i](或diff < 0),则意味着我们需要至少cost[i] - gas[i]燃油到达火车站i才能到达下一站,i + 1。 cost[i] <= gas[i]diff >= 0),这是一个有效的起点,因为我们可以在没有气体的情况下开始,填满并前往下一站。在最坏的情况下,我们将使用空罐到达下一站。充其量,当我们达到i + 1(diff > 0

时,我们将剩余额外的燃料

我们实际上不必从一个站开始,成功遍历n个加油站以查明是否有有效的旅行!只要总和燃料&gt; =总成本,就会有一个有效的旅游。所以我们只需要对数组进行O(n)传递

更多分析:

案例1:坦克跌破0

这只会发生在diff < 0的停靠点。之后可能还有另一个起点,它在经过一轮通过该站后收集足够多的燃料。但是,我们之前通过的所有站点都没有用,所以我们不需要考虑它们(看案例2的解释)。

案例2:Tank目前&gt; = 0,但我们遇到另一个有效的起点

我们可以放心地忽略这一点,因为:

A - B - C.如果B可以达到C,A可以达到B,则A可以达到C.

案例3:Tank目前&gt; = 0,不是有效的起点

继续前进到下一个

案例4:管理到达原始起点!

耶!

def find_starting_station(gas, cost):
    sum_gas = sum_cost = tank = start = 0

    for i in range(0, len(gas)):
        sum_gas += gas[i]
        sum_cost += cost[i]
        tank += gas[i] - cost[i]

        if tank < 0:
            tank = 0
            start = i+1

    if sum_gas < sum_cost: 
        return -1 

    return start

答案 3 :(得分:1)

这基本上是最大的子阵列总和问题。另一方面,我们可以从不同的POV来看待它。如果我们从第一个加油站开始旅程,让我们找出燃料最不足的地方。由于我们知道到达这一点应该占用大部分燃料,我们可以得出结论,卡车必须从这一点开始,以尽量减少负的燃料平衡。 下面是具有O(N)时间和O(1)空间复杂度的驱动程序的解决方案,并且不需要任何DP,因为一切都在一次通过中完成,并且仅使用两个整数来存储起点索引和它的价值(虽然这仅用于印刷目的)。

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <vector>
#include <algorithm>

using namespace std;

int gasoline[] = {8, 6, 30, 9, 15, 21, 2, 18};
int stations[] = {15, 8, 2, 6, 18, 9, 21, 30};

int rnd_num(const int& low, const int& high)
{
    int rndnum = (int) (((double) rand() / (double) RAND_MAX) * (high - low + 1) + low);
    return rndnum;
}

void swap(int data[], const int& idxlow, const int& idxhigh)
{
    int tmp = data[idxlow];
    data[idxlow] = data[idxhigh];
    data[idxhigh] = tmp;
}

void print_array(const char* label, int data[], int size)
{
    printf("%-10s: ", label);
    for (int i = 0; i < size; ++i){
        printf("%-3d ", data[i]);
    }
    printf("\n");
}

void print_vector(const char* label, const vector<int>& data)
{
    printf("%-10s: ", label);
    for (vector<int>::size_type i = 0; i < data.size(); ++i){
        printf("%-3d ", data[i]);
    }
    printf("\n");
}

void shuffle(int data[], int size)
{
    for (int i = 0; i < size - 1; ++i){
        int idx = rnd_num(i + 1, size - 1);
        swap(data, i, idx);
    }
}

void run(int gas[], int dist[], int size)
{
    vector<int> path;
    int diff = 0, vidx, minidx = 0, minval = gas[0] - dist[0];

    path.resize(size);

    for (int i = 0; i < size; ++i) {
        diff += gas[i] - dist[i];
        if (i == size - 1){
            vidx = 0; //path[0] = diff;
        }
        else {
            vidx = i + 1; //path[i + 1] = diff;
        }

        path[vidx] = diff;
        if (diff < minval) {
            minval = diff;
            minidx = vidx;
        }
    }

    print_vector("PATHS   ", path);
    printf("MINIDX: %d\nMINVAL: %d\n", minidx, minval);
}

int main()
{
    int size = sizeof(stations)/sizeof(stations[0]);
    srand((unsigned)time(NULL));

    shuffle(gasoline, sizeof(gasoline)/sizeof(gasoline[0]));
    shuffle(stations, sizeof(stations)/sizeof(stations[0]));

    print_array("GASOLINE ", gasoline, sizeof(gasoline)/sizeof(gasoline[0]));
    print_array("STATIONS ", stations, sizeof(stations)/sizeof(stations[0]));

    run(gasoline, stations, size);

    return 0;
}

答案 4 :(得分:0)

从任何一个站点开始,尝试移动到下一个站点(右侧)。如果你不能(用完汽油),我们必须从起点左侧取气(将起动站移到左侧)。

    start = 0
    end = start
    amount = 0
    for i in range(n):
        if amount > 0:
            amount += (gas[end] - cost[end])
            end = (end + 1) % n
        else:
            start = (start - 1 + n) % n
            amount += (gas[start] - cost[start])

    if amount >= 0: return start    
    return -1

`

答案 5 :(得分:0)

这是我在python中的解决方案。除了其他解释,一旦我们保留一个变量来计算前一站的需要,当我们在阵列的末尾时,我们只检查我们的剩余物是否高于该需要并相应地返回。

    if not gas or not cost:
        return - 1

    index = 0
    start = 0
    total = 0
    need = 0
    while index < len(gas):
        # If we can travel without any leftover.
        # What is our status since start, if total is
        # below zero that means we are in a worse situation
        # then we were.
        total += gas[index] - cost[index]
        if total < 0 :
            need -= total
            start = index + 1
            total = 0
        index += 1

    if total - need >= 0:
        return start
    else:
        return -1

答案 6 :(得分:0)

此问题的解决方案基于贪婪算法。它基于以下两个观察结果。

  
      
  1. 如果总气体>成本,必须有一个开始指数来完成圆圈,否则就没有了;

  2.   
  3. 对于索引i,如果来自i,j是我们无法达到的第一个索引,那么从i到j的任何索引都不能是起始索引。

  4.   

有关详细说明,请查看以下链接 - Gas Station problem.

答案 7 :(得分:0)

我们得到2个数组,一个数组表示双层Bi上可用的气体量,另一个数组表示我们花费从i到i + 1的气体量。 我们尝试从第一个掩体开始,并假设它是解决方案。甚至当我们看到仓中的残余气体+可用气体<需要的气体量时,我们将解决方案重置为下一个指标,重复该过程,直到达到起始点(我们最后假定的解决方案)

public int getStationIndex() {
    int answer = -1;
    if (gasAvailableArray.length != gasExpenditureArray.length) {
        throw new IllegalArgumentException("Invalid input array provided");
    }

    Queue<Integer> queue = new ArrayDeque<>();
    int residue = 0;
    for (int index = 0; ; ) {
        if (index >= gasAvailableArray.length) {
            index = index % (gasAvailableArray.length - 1);
        }
        if (index == answer) {
            return answer;
        }
        if (residue + gasAvailableArray[index] - gasExpenditureArray[index] >= 0) {
   // Only set a new answer when we had a reset in last iteration
            if (answer == -1) {
                answer = index;               
            }
            residue += gasAvailableArray[index] - gasExpenditureArray[index];
            queue.add(index);
        } else {
            while (!queue.isEmpty()) {
                queue.poll();
            }

        }
        answer = -1;
        index++;
    }
}

答案 8 :(得分:0)

static int findIndex(int A[], int B[]) {
    int N = A.length;
    int start=0;
    boolean isBreak=false;
    int total = 0;
    do {
        for(int i=start;i<start+A.length;i++) {         
            int c = A[i%N] - B[i%N];
            total +=c;          
            if(total < 0) {
                total= 0;
                isBreak=true;
                break;
            }                   
        }
        if(isBreak) {
            start++;
            isBreak=false;
        } else {
            return start;
        }
    } while(start < A.length);
    return -1;
}

答案 9 :(得分:0)

static int find(int A[], int B[]) {     
    for (int start = 0; start < A.length; start++) {
        int x = findIndex(A, B, start);
        if (x != -1)
            return x;
    }
    return -1;
}

static int findIndex(int A[], int B[], int start) {
    int total=0;
    int N = A.length;
    for (int i = start; i < start + A.length; i++) {
        int c = A[i % N] - B[i % N];
        total += c;
        if (total < 0) {
            total = 0;
            return -1;
        }
    }
    return start;
}

测试用例:

  1. 测试用例A : { 1, 2, 3, 4, 5 }, B : { 1, 3, 2, 4, 5 }->索引2
  2. 测试用例A : {2,2,1}, B : {2,1,2}索引-> 0
  3. 测试用例A : {1,2}, B {2,1}->索引1
  4. 测试用例A : {1,2,1}, B {2,1,3}->索引-1

答案 10 :(得分:0)

static int getIndex(int A[], int B[]) {
    int start = -1;
    int N = A.length;
    for (int i = 0; i < N; i++) {
        int c = A[i] - B[i];
        if (c >= 0) {
            int j = i + 1;
            while (c >= 0 && j < i + N) {
                c += A[j % N] - B[j % N];
                j++;
            }
            if (c >= 0) {
                start = i;
                break;
            }
        }
    }
    return start;
}