Unique Paths
Bir robot, m satır ve n sütundan oluşan bir ızgaranın sol üst hücresinde başlar ve sağ alt hücreye ulaşmalıdır. Her hamlede bir hücre sağa veya bir hücre aşağıya gider. Gidebileceği farklı yolların sayısını döndür.
Fonksiyon
- minteger
- ızgaradaki satır sayısı
- ninteger
- ızgaradaki sütun sayısı
- Döndürürinteger
- sol üst hücreden sağ alt hücreye giden farklı yolların sayısı
Kısıtlar
1 ≤ m, n ≤ 100- Yanıt en fazla
2 × 109olduğundan, işaretli 32 bitlik bir tam sayıya sığar.
Örnekler
- Girdi
- m = 3n = 4
- Çıktı
- 10
- Açıklama
- Her yol 2 aşağı ve 3 sağa hareket eder; toplamda 5 hareket vardır. Bir yol, aşağı giden 5 hareketten hangilerinin seçildiğiyle belirlenir ve bunları seçmenin 10 yolu vardır.
- Girdi
- m = 1n = 6
- Çıktı
- 1
- Açıklama
- Tek bir satırla robot yalnızca 5 kez sağa hareket edebilir, bu nedenle tam olarak bir yol vardır.
- Girdi
- m = 4n = 5
- Çıktı
- 35
- Açıklama
- Her yol 3 aşağı ve 4 sağa hareket içerir. 7 hareketin hangilerinin aşağı olacağını seçmek, 7 × 6 × 5 / 6 = 35 yol verir.
Gönderirken +14 gizli test
Ek soru
100 × 100'lük bir ızgara için yanıt 59 basamaklıdır. 10^9+7 modülüne göre sonucu, i'ye bölme artık işe yaramadığında formülle nasıl döndürürsünüz?
İpuçları
Tek tek açın. Her biri biraz daha fazlasını gösterir.
Robot, bir hücreye adım atmadan hemen önce nerede olabilir?
Bir hücreye giden yollar, üstündeki hücreye giden yollar ile solundaki hücreye giden yolların toplamıdır. En üst satırda ve en sol sütunda tam olarak birer yol vardır.
Sayıları satır satır, soldan sağa doldur ve tek bir sayı satırı oluştur. Ya da hamle sıralarını doğrudan say: bir yol,
m-1aşağı giden hamleninm+n-2hamle arasından seçilmesidir.
Çözüm
Yolları tek tek listelemek mümkün değil: 17 × 17'lik bir ızgarada şimdiden 601,080,390 yol var. Listelemeden saymanız gerekir. Bir hücreye giden yolların sayısı, üstündeki hücreye giden yolların sayısı ile solundaki hücreye giden yolların toplamıdır; böylece ızgara, tek geçişte dolduracağınız bir tabloya dönüşür. Bir yol aynı zamanda yalnızca aşağı ve sağ yönlerde atılan adımlardan oluşur; bu da kapalı bir formül verir.
Özyineleme ile her yolu sayın
Doğru, ama en büyük testlerde bitmiyor
Sezgi
Robotun sağ alt hücreye yaptığı son hareketi düşünün. Ya üstteki hücreden aşağıya ya da soldaki hücreden sağa geldi; ikisini birden yapmadı. Dolayısıyla m × n boyutundaki bir ızgaradaki yollar, bir satır daha kısa olan ızgaradaki yolların, uniquePaths(m-1, n), toplamı ile bir sütun daha dar olan ızgaradaki yolların, uniquePaths(m, n-1), toplamıdır.
Özyineleme, robotun yalnızca dümdüz ilerleyebildiği tek satırlı veya tek sütunlu bir ızgarada durur; bu durumda tam olarak 1 yol vardır. Her yol iki hareketten biriyle sona erdiğinden, her yol bir kez sayılır ve toplam doğrudur.
Yavaştır; çünkü her yol 1 döndüren bir temel duruma ulaşır, dolayısıyla çağrıların sayısı en az yanıtın kendisi kadardır. 17 × 17 boyutundaki bir ızgara 600 milyondan fazla çağrı gerektirir ve testlerde yanıtlar yaklaşık 1.6 × 10^9 değerine kadar çıkar. Aynı küçük ızgaralar birçok kez hesaplanır: (m-1, n-1) bu ızgaranın iki ebeveyninin her birinden bir kez erişilir ve aşağı indikçe tekrarlar katlanarak artar.
Algoritma
mveyan1 ise 1 döndür: tek yol düz bir çizgidir.- Son hamlesi aşağı olan yolları say,
uniquePaths(m-1, n). - Son hamlesi sağa olan yolları say,
uniquePaths(m, n-1). - Toplamlarını döndür.
def uniquePaths(m, n):
# One row or one column: the only path is a straight line
if m == 1 or n == 1:
return 1
# The last move came down from the row above or right from the column before
return uniquePaths(m - 1, n) + uniquePaths(m, n - 1)Izgarayı her seferinde bir satır doldurun
Sezgi
Özyineleme aynı hücreleri tekrar tekrar sorgular ve yalnızca m × n hücre vardır. İhtiyacınız olan hücrelerin her zaman hazır olacağı bir sırayla, her hücreye giden yolları bir kez sayın.
Durum: paths[r][c], sol üst hücreden r. satır, c. sütuna giden yolların sayısıdır. Özyineleme bağıntısı: paths[r][c] = paths[r-1][c] + paths[r][c-1]; yukarıdan gelen yollar ile soldan gelen yolların toplamı. Taban durumları: üst satırdaki ve sol sütundaki her hücreye giden 1 yol vardır; bu, düz bir çizgidir. Sıra: satır satır, soldan sağa; böylece ihtiyaç duymadan önce üstteki ve soldaki hücreler doldurulmuş olur.
m = 3 ve n = 4 için satırlar önce 1 1 1 1, sonra 1 2 3 4, ardından 1 3 6 10 olur ve yanıt son hücredeki 10'dur.
Şimdi doldurma işleminin neleri okuduğuna bakın: yalnızca üstteki satırı ve doldurduğunuz satırı. Bu yüzden tek bir satır tutun. row[c] değerini güncellemeden önce, bu değer hâlâ üstteki satırın sayısını tutar ve row[c-1] soldaki yeni sayıyı zaten tutar; dolayısıyla row[c] += row[c-1] tüm özyineleme bağıntısını ifade eder. Çalışma süresi O(m × n) olarak kalır ve bellek kullanımı O(m × n) değerinden O(n) değerine düşer.
Algoritma
row'unelemanlı, tüm elemanları 1 olan üst satır olarak oluştur.- Üst satırın altındaki her satır için bir kez olmak üzere
m-1kez tekrarla. - Her satırda,
ciçin 1'denn-1'e kadarrow[c-1]değerinirow[c]değerine ekle.row[0]1 olarak kalır: bu, sol sütundur. row[n-1]değerini döndür.
def uniquePaths(m, n):
# row[c] counts the paths into column c of the current row.
# The top row is all 1s: the only way along it is straight right.
row = [1] * n
for _ in range(m - 1):
for c in range(1, n):
# Paths from above (the old row[c]) plus paths from the left (the new row[c-1])
row[c] += row[c - 1]
return row[n - 1]Hamleleri bir binom katsayısıyla sayın
Sezgi
Her yol tam olarak m-1 aşağı ve n-1 sağa hareket, toplamda m+n-2 hareket yapar; bu hareketler herhangi bir sırada olabilir. Her sıralama geçerli bir yoldur: robot hiçbir zaman m-1'den fazla aşağı veya n-1'den fazla sağa hareket etmez, dolayısıyla ızgaranın dışına çıkmaz. Bu nedenle bir yol, m+n-2 hareketten hangilerinin aşağı gideceğini seçmekle aynı şeydir ve cevap binom katsayısı C(m+n-2, m-1) olur.
Önceki yaklaşımdaki tablo, yan çevrilmiş Pascal üçgenidir; bu yüzden ikisi aynı sonucu verir. Katsayıyı devasa faktöriyeller hesaplamadan bulmak için, çarpımı her seferinde bir çarpan ekleyerek oluştur. N = m+n-2 ve k = min(m, n)-1 iken, i değeri 1'den k'ye kadar giderken N-k+i ile çarp ve ardından i'ye böl. i adımından sonra biriken değer C(N-k+i, i) olur; bu bir tam sayıdır, dolayısıyla her bölme tam sonuç verir.
m = 3 ve n = 4 için: N = 5, k = 2 olur ve değer önce 1 × 4 / 1 = 4, ardından 4 × 5 / 2 = 10 şeklinde ilerler. Daha kısa kenarı seçmek döngünün 99 adım veya daha az sürmesini sağlar. Son bölmeden önceki çarpım, cevap değerinin k katıdır. 17 × 17 boyutundaki bir ızgarada bu, 16 × 601,080,390, yani yaklaşık 9.6 × 10^9 eder; bu değer 32 bitlik aralığı aşar, bu yüzden 64 bitlik bir tamsayı kullan.
Algoritma
- Hareket sayısı olan
N = m+n-2değerini vek = min(m, n)-1değerini ayarlayın. - 64 bitlik sayacı 1'den başlatın.
iiçin 1'denk'ye kadar sayacıN-k+iile çarpın, ardındani'ye bölün.- Sayaç değerini döndürün.
def uniquePaths(m, n):
# A path is m+n-2 moves; count the ways to choose which of them go down.
# Choose along the shorter side so the loop stays short.
moves = m + n - 2
k = min(m, n) - 1
count = 1
for i in range(1, k + 1):
# count goes from C(moves-k+i-1, i-1) to C(moves-k+i, i); the division is exact
count = count * (moves - k + i) // i
return count
Tuzaklar ve uç durumlar
Sayma işlemi kısa olduğundan hatalar, ızgaranın kenarlarında ve sayıların büyüklüğünde gizlenir.
(m+n-2)!değerini hesaplayıp diğer iki faktöriyelin çarpımına bölmek, yanıt taşmadan çok önce taşmaya neden olur: 21! zaten 64 bitlik aralığı aşar ve 100 × 7 boyutundaki bir ızgaradam+n-2değeri 105'e ulaşır.count / i * (N-k+i)ifadesindeki gibi çarpmadan önce bölmek,counther zamanideğerinin katı olmadığından kesirli kısmın atılmasına yol açar. Önce çarpın: çarpım her zaman tam bölünür.count × (N-k+i)çarpımı, yanıt aşmasa bile 2^31'i aşabilir. Sonucu 64 bitlik bir tamsayıda tutun.- Üst satırı veya sol sütunu 1 yerine 0 bırakmak her hücrenin 0 olmasına neden olur. Tek satırı veya tek sütunu olan bir ızgarada tam olarak 1 yol vardır.
- Satırlarla sütunların yerini değiştirmek yanıtı değiştirmez; çünkü
C(m+n-2, m-1) = C(m+n-2, n-1).
Sıkça sorulan sorular4
Unique Paths formülü nedir?
Yanıt, C(m+n-2, m-1) binom katsayısıdır. Her yol, belirli bir sırayla m-1 aşağı ve n-1 sağa hareket içerir; aşağı giden m+n-2 hareketten hangilerinin seçileceği yolu belirler. 3 × 4'lük bir ızgara için C(5, 2) = 10.
Unique Paths'in zaman karmaşıklığı nedir?
Dinamik programlama tablosu O(m × n) zaman ve tek bir satır tuttuğunuzda O(n) alan gerektirir. Binom formülü O(min(m, n)) zaman ve O(1) alan gerektirir. Basit özyineleme, yol sayısı kadar veya daha fazla çağrı yapar; bu sayı m + n açısından üstel büyür.
Bazı hücreler engelliyken Benzersiz Yollar problemini nasıl çözersiniz?
Aynı tabloyu kullanın ve engellenmiş bir hücrenin sayısını 0 olarak ayarlayın; böylece hiçbir yol oradan geçmez. Üst satır ve sol sütundaki tüm değerler artık 1 olmaz: üst satırda engellenmiş bir hücreden sonraki her hücreye giden yol sayısı 0'dır. Formül artık işe yaramaz, çünkü her hareket sırasının mümkün olduğunu varsayar.
Unique Paths tablosu neden Pascal üçgeniyle eşleşiyor?
Her hücre, üstündeki hücre ile solundaki hücreyi toplar; bu, Pascal üçgenini köşegenleri boyunca okuyarak oluşturan kuraldır. r satırındaki ve c sütunundaki hücre C(r+c, r) değerini tutar; dolayısıyla sağ alt hücre C(m+n-2, m-1) değerini tutar.
Benzer problemler
Aynı fikirleri kullanan problemler. İki üçünü çözmek bir kalıbı kalıcı hale getirir.
Python
def uniquePaths(m, n):
# Kodu buraya yazınDurum 1
Durum 2
Durum 3
Girdi
m = 3 n = 4
Beklenen
10