2017-08-10 39 views
5

問題15的所有可能的向下和右邊緣的路徑:
以2×2網格的左上角開始,有6個路由(無回溯)到右下角。
通過20×20網格有多少條路線?獲取在一個N×N的網格

所以我在Problem 15嘗試是有點bruteforcy因爲我嘗試由右到左,改變方向的第一個變化的前身獲得所有可能的有效路徑的排列。例如,當我有一個2×2的網格(查看問題15鏈接圖形)時,我將採用的第一條路徑是右 - 右 - 下 - 下,最後一個我將採用的是下 - 右 - 右 - 右,這也是我的終止標準。我將可能的有效路徑添加到列表中,並使用該列表確定是否已添加有效路徑。並且爲了排列一條路徑,我會按照我之前提到的那樣做:我從數組的右邊到左邊(圖中的箭頭指向右下角),並更改其中的第一個元素下一個元素與本身不同。所以右 - 右 - 下 - 下會變成右 - 右 - 右 - 下,這顯然是無效的,因爲你必須有相同數量的權利和波動才能到達角落。所以我認爲是從左向右進行另一個循環,根據需要更改儘可能多的元素以獲得有效的路徑。所以在這個例子中右 - 右 - 右 - 下變成下 - 右 - 右 - 下

另外,我忘記的是,我不算點,我從左上角到右下角計算邊緣。

所以我已經寫了一些代碼,但它根本不起作用。

package projecteuler; 

import java.util.ArrayList; 

public class Projecteuler { 
    public static final int GRIDSIZE = 2; 

    public static void main(String[] args) { 
     ArrayList<boolean[]> paths = new ArrayList<>(); 

     paths.add(new boolean[GRIDSIZE * 2]); 
     for(int i = 0; i < GRIDSIZE; i++) { 
      paths.get(0)[i] = true; 
      paths.get(0)[GRIDSIZE * 2 - 1 - i] = false; 
     } 

     boolean[] buf = paths.get(0).clone(); 
     printArr(buf); 
     boolean tmp; 
     while(!checkTerminate(paths)) { 
      while(paths.contains(buf)) { 
       tmp = buf[buf.length - 1]; 
       for(int i = buf.length - 1; buf[i - 1] != tmp && 0 < i; i--) { 
        buf[i] = !buf[i]; 
        for(int j = 0; checkValid(buf) && j < i; j++) 
         buf[j] = !buf[j]; 
       } 
      } 
      paths.add(buf.clone()); 
      printArr(buf); 
     } 
     System.out.println(paths.size()); 
    } 

    public static boolean checkTerminate(ArrayList<boolean[]> paths) { 
     boolean[] endPath = new boolean[GRIDSIZE * 2]; 
     for(int i = 0; i < GRIDSIZE; i++) { 
      endPath[i] = false; 
      endPath[GRIDSIZE * 2 - 1 - i] = true; 
     } 
     return paths.contains(endPath); 
    } 

    public static boolean checkValid(boolean[] arr) { 
     int countR = 0, 
      countL = 0; 
     for(int i = 0; i < arr.length; i++) 
      if(arr[i]) 
       countR++; 
      else 
       countL++; 

     return countR == countL; 
    } 

    public static void printArr(boolean[] arr) { 
     for(int i = 0; i < arr.length; i++) 
      System.out.print(arr[i] ? "right " : "down "); 
     System.out.println(); 
    } 
} 

它以某種方式不會改變任何地方的任何東西。

right right down down 
right right down down 
right right down down 
right right down down ... 

等都是它的輸出。看來代碼根本不會排列我的路徑,但也不會卡在任何for循環中。我最好的猜測是我的功能標準被放置在錯誤的序列中

我還想到了一種回溯解決方案,就像我們兩年前在學校爲迷宮做的那樣,但我想看看這種方法是否可行或者在重做所有內容之前。

編輯:
我會盡力盡快落實2×2格例子的圖像,但歐拉計劃網站正在維護的時刻。

+0

最後,呈現出實際的努力,在發佈前解決該問題的編程挑戰的問題... – meowgoesthedog

+1

問題是**很差**寫的。儘管在發佈堆棧溢出問題之前已經做了必要的努力,但帖子的標題和描述問題的方式是荒謬的:您不應該假定人們已經知道您正在討論的問題。與真正問題的外部聯繫是不可接受的。編輯帖子並在此處將所有必要的詳細信息包含在說明中,並將標題修改爲真正簡潔地描述真正問題的內容。 – progyammer

+0

@meowgoesthedog謝謝你注意到我的努力,但不知何故人們仍然在低調我的帖子。也許我的方法是太愚蠢,甚至沒有認真對待haha – LordScrat

回答

2

解決方案是由我們可以擁有的「下」和「右」移動的組合數量給出的。由於沒有回溯,所以總共有N向下和N向右移動(在任何路線中,對於NxN網格)。總共有2N動作。

我們可以使用二項式係數獲得此,ÑÇř(發音爲「N選擇R」),這是從n對象(其每一個可以是選擇r對象的方式的數量兩件事情)。在我們的例子中,「對象」是向下或向右移動。這是由

enter image description here

中所提供我們想要的號碼是:

enter image description here

N = 2這給6。對於N = 20這給出137846528820

2

讓在正確的一步被稱爲R和降壓被稱爲D.

爲了從左上角到右下角上的行和m列的網格,你會到達必須正確地執行m次並下降n次。

本質上,你將不得不得到所有可能的m R和n D的安排。


例子:對於一個2×2格,字RRDD的獨特排列的數量將是方法,使你可以走了,即數量。

  • RRDD
  • RDRD
  • DRDR
  • 復員

谷歌的公式計算,其由下式給出的與重複,字母排列:

N! /(r !* r !...),其中所有r的和爲n。

This question關於Math SE在尋找重複字母排列計數時首先彈出,second answer在我看來更好解釋。


因此,要返回計數並且甚至要返回路徑,您根本不需要遍歷迷宮。只需做第一個公式計算並打印第二個問題的排列。

當某些步驟離開網格時,給出路徑將是唯一要求您實際遍歷迷宮的情況。


UPDATE:

因爲可以把重複的字母排列的公式。

下面是演示該案例的幻燈片。看看2E在產生排列時最終如何重複排列。一般而言,重複的任何字母將導致r!因爲無論放在哪個字母的排列中,都可以用另一個相同的字母來替換,而不用重新排列。

這樣,如果我們將總數n分開!與r!排列,我們得到了實際獨特的排列。

Repeated letter permutations

Image Source