[白俊]1520番下り坂



  • 移動:上下左右にしか移動できません.

  • (0,0)->(m-1,n-1)に移動しようとします.

  • 高さがもっと低いところに移動したいだけです.

  • このとき移動可能な[パス数]

  • トラブルシューティングプロセス
  • bfsを使います.
  • という質問は「経路の個数」を要求する必要があるため、アクセスの有無を別途チェックする必要はないのでしょうか???
  • でしょう、もちろん、この経路ではすでに通っている場所に繰り返しアクセスすることはできません...あ、だからbfsではなくdfsを使うべきかもしれません.
  • visitを使用します.
  • bactkrackの場合、アクセスは解除されます.
  • の各深さのノードには同じパスがないからです.
  • 時間の複雑さ...ほぼ探索に近い遡及Kingでは….n,mは500以下である...これはだめみたいです.
  • bfsで問題を解決すれば、アクセスの有無を別途チェックしなくても返されません.「もっと低いところまでしか行かない!移動するので
  • ですが、メモリオーバーフローが予想されます.visitは別途チェックしていないので25000^3の繰り返し
  • が発生します

    間違っています-->DPでも解けるみたい:PriorityQueueのDPで解ける

  • トラブルシューティング2
  • 「下り坂」なので、高いところから低いところへしか移動できません.
  • すなわち、任意のセルの隣接する4つのセルのうち、そのセルのパスに影響を及ぼすのは、そのセルよりも高いセルだけです.
  • がDPで問題を解決しようとすると、このような考えがあります.
  • 各グリッドに格納されるパスの個数.
  • 特定セルへのパスの数は、特定セルへのすべてのパスの合計に等しい.
  • という考え方の問題は、アクセス順です.例えば、(0,2)を求め、(1,2)を用いる.しかしこのとき(1,2)までの経路の個数は得られなかった可能性がある.(1,2)の経路を求めることなく下へ移動)
  • によれば、高い格子から対応する格子への経路を探すことだ.そのため、PriorityQueueを使用しました.キューは、(y,x,h)情報を格納する.
  • 題の例(1,1)では、上下左右に隣接するセルのうち50を超えるものはないので、そのセルへのパスは存在しない.
  • の高い格子の周りに低い格子や同じ格子しかないとしたら.その一格は届かないからだ.
  • は、隣接するセル間のパス数をそのセルへのパス数としてすべて加算する動的プログラミング技術である.
  • キャッシュ初期化
  • [0,0]セルに1を含める必要があります.たとえば、上記の例では、(1,0)のパスを求めると、cache[0,0]+cache[1,1]は0ではありません.
  • 複雑度
  • による包括的な探索PQ:O(nlogn)を入れるためにnは25000人...
  • PQから取り出したセル、すなわちすべてのセルの隣接する4つのセルは、キャッシュn*4->O(n)
  • を更新する.
        public class Main{
    
            public static int m,n;
            public static int[][] cache;
            public static int[][] table;
            public static Queue<int[]> q = new PriorityQueue<>(new Comparator<int[]>() {
                @Override
                public int compare(int[] o1, int[] o2) {
                    // height를 기준으로 내림차순
                    return o2[2]-o1[2];
                }
            });
            public static int[][] dirs = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
            public static int path =0;
            public static void Setting() throws IOException {
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                StringTokenizer st = new StringTokenizer(br.readLine());
                m = Integer.parseInt(st.nextToken());
                n = Integer.parseInt(st.nextToken());
                table = new int[m][n];
                cache = new int[m][n];
    
                for(int row=0;row<m;row++){
                    st = new StringTokenizer(br.readLine());
                    for(int col=0;col<n;col++)
                        table[row][col] = Integer.parseInt(st.nextToken());
                }
                // 0,0 칸 캐시를 1로 해야, 인접한 칸까지의 경로에 수가 더해진다.
                cache[0][0]=1;
    
                // PQ를 세팅한다.
                // 첫 번째 줄은, (0,0)칸만 제외하고 넣어준다.
                for(int col=1;col<n;col++)
                    q.add(new int[]{0,col,table[0][col]});
                for(int row=1;row<m;row++){
                    for(int col=0;col<n;col++){
                        q.add(new int[]{row,col,table[row][col]});
                    }
                }
    
            }
            public static void dp(){
                int curH=0;
                while(q.isEmpty()==false){
                    int[] cur = q.poll();
                    int path=0;
                    for(int dir=0;dir<dirs.length;dir++){
                        int y = cur[0]+dirs[dir][0];
                        int x = cur[1]+dirs[dir][1];
                        // 벗어나면 fail
                        if(y<0||x<0||y>=m||x>=n)continue;
                        // 더 높은 칸이면  path개수를 더한다.
                        if(table[y][x]>cur[2]) path+=cache[y][x];
                    }
                    //System.out.println(cur[0]+","+cur[1] +" : "+path);
                    // cache를 업데이트
                    cache[cur[0]][cur[1]]=path;
                }
            }
            public static void main(String[] args) throws IOException{
                Setting();
                dp();
                System.out.println(cache[m-1][n-1]);
            }
        }

    他の人の解題方法を見ました。DFS + DP


  • これは私の方法より効率的です.

  • 簡単なDFSを使用して再帰的検索を行う場合、DFSを使用して遡及する方法は、非常に多くの場合に関連し、タイムアウトを引き起こす.

  • しかし、ここにDPを加えれば、時間を減らすことができます.

  • ここで、cache[y][x]は、そのセルを含むパスの個数を表す.

  • ある経路で[m,n]に到達すると,その経路に属するすべてのノードが[m,n]に到達する.

  • また,dfsについては,すべての経路が異なる経路であることを覚えればよい.(パスの設定が異なる)

  • dfsを使用するbacktrackでは、各パスで渡される値によって、現在のノードを含むパスの数がわかります.(絵を見ていると理解できるようです)

  • だから、この図で理解すればいいのです.
  • 1のため、20のcacheにおける状態は1
  • である.
  • 2号経路から20に到達すると、cacheが1を有することにより、現在の経路を介して[n,m]に到達できることがわかる.
  • 
    public class Main{
    
        public static int m,n;
        public static int[][] cache = new int[501][501];
        public static int[][] table = new int[501][501];
    
        public static int[][] dirs = new int[][]{{0,1},{0,-1},{1,0},{-1,0}};
        public static int path =0;
        public static void Setting() throws IOException {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            StringTokenizer st = new StringTokenizer(br.readLine());
            m = Integer.parseInt(st.nextToken());
            n = Integer.parseInt(st.nextToken());
    
            for(int row=0;row<m;row++){
                st = new StringTokenizer(br.readLine());
                for(int col=0;col<n;col++)
                    table[row][col] = Integer.parseInt(st.nextToken());
            }
            // cache를 -1로 세팅한다. dfs를 통해서 cache에 0이 들어올 것도 존재함을 생각하라.
    				// dp는 왠만하면 -1로 세팅하고 시작한다. 
            for(int row=0;row<m;row++){
                Arrays.fill(cache[row],-1);
            }
    
        }
        public static int dfs(int y,int x){
            //System.out.println(y+","+x);
            if(x==n-1 &&y==m-1) return 1;
            if(cache[y][x]!=-1) return cache[y][x];
            int path =0;
            for(int dir =0;dir<dirs.length;dir++){
                int ny=y+dirs[dir][0];
                int nx=x+dirs[dir][1];
                // 범위를 넘으면 pass
                if(ny<0||nx<0||ny>=m||nx>=n) continue;
                // 더 낮은 곳이 아니면 pass
                if(table[ny][nx]>=table[y][x])continue;
                int temp = dfs(ny,nx);
                path+=temp;
    
            }
            cache[y][x]=path;
            return cache[y][x];
        }
        public static void main(String[] args) throws IOException{
            Setting();
            dfs(0,0);
            System.out.println(cache[0][0]);
        }
    }