web-dev-qa-db-ja.com

誰かがこの複雑なアルゴリズムの問​​題を解決するのを手伝ってくれる?

面接でこの質問を受けましたが、解決できませんでした。

  • N個のガソリンスタンドのある環状道路があります。
  • あなたは各ステーションが持っているガスの量を知っています。
  • あるステーションから次のステーションに移動するために必要なガスの量を知っています。
  • 車は0から始まります。
  • 時計回りにしか運転で​​きません。

問題は、完全な円を完成させるために運転を開始する必要があるガソリンスタンドから知るアルゴリズムを作成することです。

私への演習として、アルゴリズムをC#に変換します。

6
Luis Valencia

(更新:ガスタンクの最大サイズを許可)

これは、次のように線形時間で解決できます。

void FindStartingPoint(int[] gasOnStation, int[] gasDrivingCosts, int gasTankSize)
{
  // Assume gasOnStation.length == gasDrivingCosts.length
  int n = gasOnStation.length;

  // Make a round, without actually caring how much gas we have.
  int minI = 0;
  int minEndValue = 0;
  int gasValue = 0;
  for (int i = 0; i < n; i++)
  {
    if (gasValue < minEndValue)
    {
      minI = i;
      minEndValue = gasValue;
    }
    gasValue = gasValue + gasOnStation[i] - gasDrivingCosts[i];
  }

  if (gasValue < 0)
  {
    Console.WriteLine("Instance does not have a solution: not enough fuel to make a round.");
  }
  else
  {
    // Try a round.
    int gas = DoLeg(0, minI, gasTankSize);
    if (gas < 0)
    {
      Console.WriteLine("Instance does not have a solution: our tank size is holding us back.");
      return;
    }
    for (int i = (minI + 1) % n; i != minI; i = (i + 1) % n)
    {
      gas = DoLeg(gas, i, gasTankSize);

      if (gas < 0)
      {
        Console.WriteLine("Instance does not have a solution: our tank size is holding us back.");
        return;
      }
    }
    Console.WriteLine("Start at station: " + minI);
  }
}
int DoLeg(int gas, int i, int gasTankSize)
{
  gas += gasOnStation[i];
  if (gas > gasTankSize) gas = gasTankSize;
  gas -= gasDrivingCosts[i];
  return gas;
}

最初に、最大のガソリンタンクがない場合を見てみましょう。

基本的に、最初のforループでは、燃料タンクに負の燃料が含まれているかどうかを気にせず、円を回ります。ここでのポイントは、どこから始めても、燃料タンクの開始時(0)と終了時の量の差は同じであることです。

したがって、最初よりも少ない燃料(つまり0未満)で終了する場合、これはどこから始めても発生するため、完全に一周することはできません。

完全な円を進んだ後、開始時と同じ量以上の燃料が残っている場合は、燃料タンクが最低点にある瞬間を探します(これは常にガソリンスタンドに到達するときと同じです)。この時点から始めれば、この時点よりも燃料が少なくなることは決してありません(それが最低点であり、円を運転しても燃料が失われないためです)。

したがって、この点は有効な解決策であり、特に、そのような点は常に存在します。

ここで、ガソリンタンクがそれほど多くのガスしか収容できないバージョンを見てみましょう。

最初のテスト(上記で説明)で、サークル全体に行くのは不可能ではないことがわかったとします。ガソリンスタンドiから始め、ガソリンスタンドjにタンクを入れると仮定しますが、ガソリンタンクが満杯になるため、ステーションにある余分なガスを逃します利用可能です。次に、ステーションに到着する前にkを実行しましたが、見逃したガスのために、燃料が不足しています。

このシナリオでは、これはどこから始めても起こります。ステーションlから開始するとします。

ljkの間にある場合は、駅に到着する前に(長い)停止するk悪いステーションから始めたか、iから始めようとしたときにkに到達しようとしたときに常に持っていた量の燃料が常にあるからです。同じステーションを通過したため(そしてjを通過したとき、タンクは満タンでした)どちらのケースも悪いです。

ljkの間にない場合、jに到達する前に(long)を停止するか、またはjに到達し、最大で満タンになります。つまり、kにも到達しません。どちらのケースも悪いです。

これは、無限に大きいガソリンタンクの場合のように、最低点からラウンドを開始した場合、成功するか、ガソリンタンクが小さすぎたために失敗することを意味しますが、それは関係なく失敗します。最初に選択するステーション。つまり、インスタンスにはソリューションがありません。

23
Alex ten Brink

これは最短経路アルゴリズムの派生物のように見えるので、基本的に各測点はグラフの頂点であると見なし、エッジは(燃料に関して)距離に重み付けされます。

次に、ダイクストラの適応を使用してそれを解決できます-> http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm

9
Daniel Scocco

私の最初の応答は、「要件が不完全であるように見えます。これらの要件のみに基づいてコードを記述してもよろしいですか、それとも私たちがそれらを作成していくのでしょうか、それとも設計前に要件の完了を手助けしてくれますか?ソフトウェア?」.

不完全な要求は、他の質問で述べられているように、車の燃料容量です。終点-始点または始点の前の測点です。 (コメントでこれに答えると思います。「この場合、完全な円は終了=開始を意味します。明示的ではないので、これを明確にします)質問は、これが正しいことを示し、1つの解決策しかありません。

その他の問題アルゴリズムのパフォーマンスが問題か、言い換えれば、このプロジェクトの予算はどれくらいか。

これは良い質問です。現在の形式では答えられず、顧客が期待している問題の解決策を顧客に提供したかどうかを確認することはできません。

5
mattnz

与えられたクイズにはいくつかの重要な部分が欠けているような気がします。ただし、私の推論では、次の仮定を使用します。

  • 車のタンクは無制限の燃料容量を持っています
  • 時計回りにのみ移動できます
  • 車は0ガスで始まる
  • 車は同じガソリンスタンドで終了する必要があります

この仮定を前提として、すべてのガソリンスタンドを試すことで、O(n ** 2)の複雑さでタスクを簡単に強制できます。

より堅牢なソリューションが必要な場合は、このクイズを 最大連続サブシーケンス問題 に変換して、ゼロを下回らないようにすることができます。このアルゴリズムの複雑さは、O(n)-線形です。

最初のステップとして、最初の問題を、ガソリンスタンドでのガスの量とそのガソリンスタンドに行くためのコストを合計することによって、最大のサブシーケンスによって解決可能な問題に変換します。

2番目のステップは、最大サブシーケンスアルゴリズムを適用することです。アルゴリズムは2つの仮定に基づいています。

  • ガソリンカウンターが0未満になった場合は、常に現在のガソリンスタンドから再開することをお勧めします
  • それ以外の場合は、ここではガスが0を超えるため、タンクを埋めるだけの方が良い

このアルゴリズムをステーションの円で囲まれたリストに2回(2つのオーバーラップシーケンス)に適用すると、ほとんどの燃料を蓄積するシーケンスを開始するステーションが取得されます。

3番目のステップは、与えられた量の燃料と与えられた開始ステーション、複雑さo(n)で目標に到達できることを確認することです

5
Valera Kolupaev

以下は私がそれがうまくいくと思う方法の説明です。これが唯一の解決策であるとは言えません。私はこれを完全にテストしなかったので、検証するためにあなたにこれを任せます。

実は面接で思いつくのは難しいと思います。

添付の図は、一般的な考え方を示しています。変数xは、調査中の現在のノードを表します。 enter image description here

最初のノードの場合は、この式を使用します。

enter image description here

ノードx = 2、...、Nにこの式を使用します

enter image description here

各ノードにはガスの量がありますg(x)そしてノードxから次のノードまでの距離はd(x、x + 1)で与えられます)

上記の方程式のR.H.Sは、ルートの最後の2つのノード間の距離でなければなりません。

有効なルートの順列を生成することから始めます-例:

ルート1:A、B、C、D、A

ルート2:B、C、D、A、B

ルート3:C、D、A、B、C

ルート4:D、A、B、C、D

各ルートについて、特定のルートの任意のノードxで、次のノードに移動できるかどうかを計算する必要があるという考え方です。これは次のように決定されます。

[ステップ0]これまでのガスの量+現在のステーションで使用できる最大ガスを確認します。上記の量で次のステーションに移動でき、このノードをルートに追加できます。

[ステップ1]上記の量が十分でない場合は、現在の開始点が不良です。別のルートを試してください。

ここで、ノードxにあるガスの量を計算するために、消費するガスの各ユニットについて、1ユニットの距離を移動すると仮定します。

各ガスストップから満たされたすべてのガスを合計します(g1 + g2 + ... + g(x-1))-すべての移動距離を合計します(d1 + d2 + ... + d(x-1))+上記に追加このステーションのガスの合計(g(x))

例1-提供されたデータの使用:

ルート1、A、B、C、D

B(1)の計算:

g(1)= 4

d(1,2)= 5

B(1)= false。これは、A、B、C、Dのルートを選択するのは適切ではないことを意味します。

例2:

ルート2、B、C、D、A

B(1)の計算:

g(1)= 48

d(1,2)= 45

g(1) <= d(1,2) is false, so B(1)=false that means starting at B is working so far. Continue to examine the situation at the next node (C)

g(2)= 50

d(2,3)= 44

g(1)-d(1,2)= 48-45

50 +(48-45)<=(d(2,3)= 44)はfalseであるため、B(2)はfalseです。つまり、Bから開始してCに到達すると、これまでの作業。テストを続行して、次のノードのステータスを確認します(D)

4
NoChance

少し遅いですが、試行錯誤の方法を提案します。 (面接の内容や要件の明確化については考慮していません。)

ステーションkから始めて、可能な限り最も遠いステーションまで運転します。N + k= kに再び到達した場合、モジュラー演算モジュロ[〜#〜] n [〜#〜]、したがって= [〜#〜] n [〜#〜]1= N + 1など))、私たちは成功しました。

しかし、再びkに到達できないと仮定しますが、k<= l<N + kの場合はlのみです。 kk-1から試してみましょう。kに到達できない場合は、k-2k-3、.. 。

2つの可能性があります。

(1)残念ながら、kfrom k-1、from k-2、 ...、そして最後にkN= kから。成功する方法はありません。道路のあらゆる可能なガソリンスタンドを試しました。

(2)または、k '<kで、kkから、lに到達し、多分l '> l(ステーションkに十分なガスがある場合、到達k '再び:成功しました(a)。そうでない場合は[k'、l ']それでも、[k、l]の適切なスーパーセットです。なぜならk '<kand l'> = l(b)

がっかりしない場合は、同じ方法を繰り返し適用して(N + k境界を維持))、ルートのシーケンスを見つけることができます。これらのルートはそれぞれ長くなります(ガソリンスタンドが多くなります)以前よりも。条件(1)(失敗)または条件(2)(a)(成功)。

この単純なアルゴリズムのコーディングを考えると、kからlへのルートのコストは1回だけ計算されます。したがって、ルートの新しい部分のコスト(各ステップでk 'からkおよびlからl')まで。これはかなり高速だと思います。

:他のいくつかの回答で述べられているように、この問題はグラフを含むより複雑な問題の単純なバージョンです。参照 https://cs.stackexchange.com/questions/25906/understanding-an -algorithm-for-the-gas-station-problem

それが役に立てば幸い。

[〜#〜] edit [〜#〜]:ガスタンクを制限した上記のソリューションの実装です。ループは1つしか使用しないため、ステーションに十分なガスがない場合は少し遅くなりますが、それ以外の場合は速くなります。文字通りの説明よりもわかりやすいと思います。

int FindStartingPointInOneLoop(int[] gasOnStation, int[] gasDrivingCosts, int gasTankSize)
{
  // Assume gasOnStation.length == gasDrivingCosts.length
  int n = gasOnStation.length;
  bool move_forward = true;
  int k = 0;
  int l = 0;
  int gas = 0;
  while (true) {
    if (move_forward) { // try to move forward
      gas = DoLeg(gas, l, gasTankSize);
      if (gas < 0) { // we're out of gas : let's try a new start
        move_forward = false;
      }
      l = (l+1) % n;
    } else { // try to start from a previous gas station
      k = (k-1) % n;
      if (k == 0) { // that was a complete round without any adequate start
        Console.WriteLine("Instance does not have a solution.");
        return -1;
      }
      gas = DoLeg(gas, k, gasTankSize);
        if (gas >= 0) { // we reached our start point, let's try to move forward with the optional extra gas
          move_forward = true;
      }
    }
    if (l == k && gas >= 0) { // we joined "head" and "tail"
      return k;
    }
  }  
}
int DoLeg(int gas, int i, int gasTankSize)
{
  gas += gasOnStation[i];
  if (gas > gasTankSize) gas = gasTankSize;
  gas -= gasDrivingCosts[i];
  return gas;
}
1
jferard