これは最近私が知っている誰かに尋ねられました。
ロボットは、マトリックスの形をしたグリッド内を移動する必要があります。行くことができます
- A(i,j)--> A(i+j,j) (Down)
- A(i,j)--> A(i,i+j) (Right)
(1,1)で始まり、A(m、n)に移動する必要がある場合、(m、n)に到達して書き込むために必要な最小ステップ数を見つけます。
public static int minSteps(int m,int n)
たとえば、(1,1)からm = 3およびn = 2に移動するには、(1、1)->(1、2)->(3、2)、つまり2つのステップを実行する必要があります
残念なことに、私の友人は、一意のパスの数を見つけるという other よく知られている質問であるかのように答えました。
M、nに到達するために取られる最小ステップ数を見つけるために解決する方法は?
「後ろに戻るとどうなるか」と尋ねると、興味深いことが起こります。
したがって、A(i + j、j)-> A(i、j)から左に移動し、A(i、i + j)-> A(i、j)から上に移動する必要があります。 iとjを使い続けると混乱が生じる可能性があるため、xとyを使用して、左または上に行くかどうかに関係なく、A(x、y)から逆方向に行くと言います。
次の式が得られます。
左:A(x、y)-> A(x-y、y)
上:A(x、y)-> A(x、y-x)
最初に注意すること:x = yの場合、両方の数式で0座標が得られるため、対角線上の開始点ではないものには到達できないことがわかります。
2番目に気づくのは、1つの数式にx-yがあり、もう1つの数式にy-xがあることです。 xとyは常に正なので、2つの場所のうち、少なくとも1つは取得できない可能性がありますです。
これは、正方形が到達不能でない限り、どの正方形から来たのかを正確に把握できることを意味します。その場合、正方形は斜めの正方形か、斜めの正方形からのものです。到達可能な正方形からずっとバックトラックすることにより、onlyパスを取得します。これは、最短のパスでなければなりません。
以下は、最初に提示された問題に対する私の解決策です。
Jを変更する方法は1つだけで、変更すると、ロボットが右に移動したため、1ずつ増加します。 jが1で始まり、nで終わることがわかっているので、jが右にいくつのステップを取ったかがわかります。
私と同じことと降格。
どの経路をとったかは関係ありません。
これは動的プログラミングの問題です。アイデアは、すべてのパスを試し、毎回実行される最小ステップ数を返すことです。
int MinSteps( int posX, int posY, int destX, int destY)
{
// if we reached the destination
if (posX == destX && posY == destY)
return 0;
// if we didn't reach the dest we need to penalize this path so we return INT_MAX
if (posX >= arraySizeX || posY >= arraySizeY )
return INT_MAX;
return min( MinSteps(posX+posY, posY), MinSteps(posX, posY+posX) ) + 1;
}
同じパスを数回再計算しているので、自明最適化は、メモ化を使用して、これまでの最小ステップを格納することです。
// Table should be init with INT_MAX except the first column with 0.
int MinSteps( int posX, int posY, int destX, int destY, int** table)
{
// if we reached the destination
if (posX == destX && posY == destY)
return 0;
// if we didn't reach the dest, we need to penalize this path so we return INT_MAX
if (posX >= arraySizeX || posY >= arraySizeY )
return INT_MAX;
// If we already calculate this paths min steps we only look it up
if (table[posX][posY] != INT_MAX)
return table[posX][posY];
table[posX][posY]= min( MinSteps(posX+posY, posY), MinSteps(posX, posY+posX) ) + 1;
}
これは、幅優先検索を使用して簡単にブルートフォースすることができます。
キューを使用する最も単純なアルゴリズム。すでに訪れたポイントを思い出すことで、パフォーマンスを向上させることができます。また、ブレスファーストを使用しているため、以前にアクセスした場所がより近くなっていることを確認できます。 (C#コードの場合は申し訳ありません)。
private struct Location
{
public int X { get; set; }
public int Y { get; set; }
public int Distance { get; set; }
}
public static int minSteps(int m, int n)
{
Queue<Location> locations = new Queue<Location>();
locations.Enqueue(new Location(){X = 1, Y = 1, Distance = 0});
while(locations.Count > 0)
{
var loc = locations.Dequeue();
// if it reached the target location
if(loc.X == m && loc.Y == n)
{
return loc.Distance;
}
// if we went past the location then this path is invalid
if(loc.X > m || loc.Y > n)
continue;
locations.Enqueue(new Location{ X = loc.X + loc.Y, Y = loc.Y, Distance = loc.Distance+1});
locations.Enqueue(new Location{ X = loc.X, Y = loc.X + loc.Y, Distance = loc.Distance + 1 });
}
throw new Exception("No path found!");
}
ここに1つあります。これは、インタビュアーが探していたものと同じであると言われました
1.)m> nの場合、m- = nです。つまり、(m-n、n)からしか取得できなかった可能性があります。
2.)それ以外の場合、m <nの場合、n- = m、つまり(m、n-m)からのみ取得できます。
その後、エッジケースとハウスキーピングが続きます
int minSteps(int m, int n)
{
if(m<=1 || n <=1) return -1;
int count = 0;
while(abs(m-n) && m>=1 && n>=1)
{
if(m>n)
m=m-n;
else n = n-m;
count++;
}
if(m==1 && n==1)
return count;
else return -1;
}
これは here から取得されます