【回溯】B052_LQ_對稱迷宮(暴搜 / 雙向 dfs)

一、Problem

wlxsq有一個N*NN∗N的網格迷宮,每一個網格都有一個字母編號。

他要從左上角(1,1)(1,1)出發,走到右下角(n,n)(n,n),由於wlxsq很懶,所以他每次只會往右或者往下走一格。由於最後到終點的路徑方案太多太多了,所以wlxsq想讓你計算出所有不同的對稱的路徑個數。

例如:N = 3N=3
ABA
BBB
ABA

對稱路徑6條:有ABABA(2條)、ABBBA(4條),不同的對稱路徑有: 有ABABA、ABBBA

輸入描述

  • 第一行輸入一個數 N NN,表示迷宮的大小。
    接下來輸入 N∗N N∗NN∗N 的字母迷宮

輸出描述

  • 輸出對稱路徑的數量
輸入
  3 33
  ABA ABAABA
  BBB BBBBBB
  ABA ABAABA
輸出
  2 22

評測用例規模與約定

  • 對於 40% 的數據,2<=N<=11
    對於 100% 的數據,2<=N<=18

二、Solution

方法一:暴搜(超時)

202\cfrac{20}{2}

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static class Solution {
		int N;
		char[][] g;
		Set<String> all;
		boolean[][] vis;
		final int[][] dir = { {1,0}, {0,1} };
		
		void dfs(int x, int y, String p) {
			if (x == N-1 && y == N-1 && check(p)) {
				all.add(p);
				return;
			} 
			for (int k = 0; k < 2; k++) {
				int tx = x + dir[k][0];
				int ty = y + dir[k][1];
				if (!inArea(tx, ty) || vis[tx][ty])
					continue;
				vis[tx][ty] = true;
				dfs(tx, ty, p + g[tx][ty]);
				vis[tx][ty] = false;
			}
		}
		boolean check(String s) {
			int l = 0, r = s.length()-1;
			while (l < r) {
				if (s.charAt(l++) != s.charAt(r--))
					return false;
			}
			return true;
		}
		boolean inArea(int x, int y) {
			return x < N && y < N;
		}
		void init() {
			Scanner sc = new Scanner(new BufferedInputStream(System.in));
			BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
			all = new HashSet<>();
			N = sc.nextInt();
			g = new char[N][N];
			vis = new boolean[N][N];
			vis[0][0] = true;
			for (int i = 0; i < N; i++) {
				String s = sc.next();
				for (int j = 0; j < N; j++)
					g[i][j] = s.charAt(j);
			}
			dfs(0, 0, g[0][0] + "");
			System.out.println(all.size());
		}
	}
    public static void main(String[] args) throws IOException {  
        Solution s = new Solution();
		s.init();
    }
}

複雜度分析

  • 時間複雜度:O(...)O(...)
  • 空間複雜度:O(...)O(...)

方法二:雙向 dfs

  • dfs1 從點 (0, 0) 遍歷到矩陣上滿足座標和滿足 x+y = N-1 的點,。
  • dfs2 從點 (N-1, N-1) 矩陣上滿足座標和滿足 x+y = N-1 的點,到達所述點之後,如果此時的路徑和之前的保存的路徑相同,那麼 res++

流下 Java 超時的淚水:爲什麼…一樣的邏輯,C++ 可以 A,看來非要我轉 C++ 了,有緣再見 Java,你虐我太狠…

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static class Solution {
		int N;
		char[][] g;
		Set<String>[] ss;
		Set<String> s2;
		int res;
		boolean[][] vis;
		final int[][] dir = { {1,0}, {0,1} };
		
		void dfs2(int x, int y, String p) {
			if (x + y == N-1 && !s2.contains(p) && ss[x].contains(p)) {
				res++;
				s2.add(p);
				return;
			} 
			for (int k = 0; k < 2; k++) {
				int tx = x - dir[k][0];
				int ty = y - dir[k][1];
				if (tx < 0 || ty < 0 || vis[tx][ty])
					continue;
				vis[tx][ty] = true;
				dfs2(tx, ty, p + g[tx][ty]);
				vis[tx][ty] = false;
			}
		}
		
		void dfs1(int x, int y, String p) {
			if (x + y == N-1) {
				ss[x].add(p);
				return;
			} 
			for (int k = 0; k < 2; k++) {
				int tx = x + dir[k][0];
				int ty = y + dir[k][1];
				if (tx >= N || ty >= N || vis[tx][ty])
					continue;
				vis[tx][ty] = true;
				dfs1(tx, ty, p + g[tx][ty]);
				vis[tx][ty] = false;
			}
		}
		void init() {
			Scanner sc = new Scanner(new BufferedInputStream(System.in));
			BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
			N = sc.nextInt();
			g = new char[N][N];
			vis = new boolean[N][N];
			vis[0][0] = true;
			ss = new HashSet[18+50];
			s2 = new HashSet<>();
			for (int i = 0; i < ss.length; i++) {
			    ss[i] = new HashSet<>();
			}
			for (int i = 0; i < N; i++) {
				String s = sc.next();
				for (int j = 0; j < N; j++)
					g[i][j] = s.charAt(j);
			}
			dfs1(0, 0, g[0][0] + "");
			dfs2(N-1, N-1, g[N-1][N-1] + "");
			System.out.println(res);
		}
	}
    public static void main(String[] args) throws IOException {  
        Solution s = new Solution();
		s.init();
    }
}

複雜度分析

  • 時間複雜度:O(...)O(...)
  • 空間複雜度:O(...)O(...)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章