# BFS# DFS# Backtracking

e585 - 12797 - Letters

🔗 前往 ZeroJudge 原題

題目描述

題目要求計算從一個 N x N 的公園左上角到右下角的最短一致路徑長度。公園的每個格子包含一個字母,可以是前 10 個大寫字母或前 10 個小寫字母。一致路徑的定義是,一旦選擇了大寫字母,後續只能走大寫字母;一旦選擇了小寫字母,後續只能走小寫字母。如果不存在一致路徑,則輸出 -1。

解題思路

本題採用深度優先搜尋 (DFS) 搭配廣度優先搜尋 (BFS) 的方式來解決。首先,使用 DFS 窮舉所有可能的字母選擇(大寫或小寫),對於每種選擇,使用 BFS 尋找從起點到終點的最短路徑。DFS 的作用是確定路徑的一致性,即所有字母都是大寫或小寫。BFS 的作用是尋找在一致性約束下,最短的路徑長度。如果 BFS 找到路徑,則更新最短路徑長度。如果 DFS 窮舉所有可能的選擇後,仍然沒有找到路徑,則輸出 -1。

複雜度分析

  • 時間複雜度: O(2^10 * N^2 * N^2)。其中 2^10 是 DFS 窮舉所有字母大小寫組合的數量,N^2 是公園的大小,N^2 是 BFS 的最壞情況複雜度。
  • 空間複雜度: O(N^2)。主要由 BFS 的隊列和 has 陣列佔用。

程式碼

#include <bits/stdc++.h>
using namespace std;
int n,w[4][2]={{0,1},{0,-1},{1,0},{-1,0}},ans;
bool cu[20],has[105][105];
char a[105][105];
void bfs(){
	int ct=0,cnt=1;
	queue <pair <int,int>> q; // used x y
	memset(has,0,sizeof(has));
	if(cu[a[0][0]]==0)return;
	q.push({0,0});
	while(!has[n-1][n-1]&&!q.empty()&&ct<ans){
		++ct;
		queue <pair <int,int>> nxt;
		while(!q.empty()){
			pair <int,int> tp = q.front();
			int x=tp.first,y=tp.second;
			q.pop();
			if(has[x][y])continue;
			has[x][y]=1;
			for(int i=0;i<4;++i){
				int xt=x+w[i][0],yt=y+w[i][1];
				if(xt>=0&&xt<n&&yt>=0&&yt<n&&cu[a[xt][yt]])
					nxt.push({xt,yt});
			}
		}
		q=nxt;
	}
	if(has[n-1][n-1])ans=ct;
	return;
}
void dfs(int it){
	if(it==10){
		bfs();
		return;
	}
	cu[it]=1;
	cu[it+10]=0;
	dfs(it+1);
	cu[it]=0;
	cu[it+10]=1;
	dfs(it+1);
}
int main(){
	cin.tie(0);cout.tie(0); ios::sync_with_stdio(false);
	while(cin >> n){
		for(int i=0;i<n;++i)
			for(int j=0;j<n;++j){
				cin >> a[i][j];
				if(a[i][j]>='a')a[i][j]=a[i][j]-'a'+'A'+10;
				a[i][j]-='A';
			}
		if(abs(a[0][0]-a[n-1][n-1])==10){
			cout << "-1\n";
			continue;
		}
		ans=10000;
		dfs(0);
		if(ans==10000){
			cout << "-1\n";
		}
		else{
			cout << ans << '\n';
		}
	}
}

Discussion