原題:
https://cses.fi/problemset/task/1194
題意:
n×m 迷宮,有牆 #、空地 .、起點 A,以及多個怪物 M。每分鐘人與怪物都能向四鄰格移動。若人走到邊界格即可逃脫,但不能和怪物同時或更晚抵達同一格。判斷能否逃出,若可輸出最短路徑長與路徑字串。
解題思路:
#include <bits/stdc++.h>
using namespace std;
const int dx[4] = {-1, 1, 0, 0};
const int dy[4] = {0, 0, -1, 1};
const char dir[4] = {'U','D','L','R'};
int main() {
int n, m;
cin >> n >> m;
vector<string> g(n);
for (int i=0; i<n; i++) cin >> g[i];
vector<vector<int>> distM(n, vector<int>(m, (int)1e9));
vector<vector<int>> distA(n, vector<int>(m, (int)1e9));
vector<vector<char>> fromDir(n, vector<char>(m, 0));
queue<pair<int,int>> qm, qa;
pair<int,int> A(-1,-1);
// 多源 BFS for monsters
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++) {
if (g[i][j] == 'M') {
distM[i][j] = 0;
qm.push(make_pair(i, j));
} else if (g[i][j] == 'A') {
A = make_pair(i, j);
}
}
}
while (!qm.empty()) {
pair<int,int> cur = qm.front(); qm.pop();
int x = cur.first, y = cur.second;
for (int k = 0; k < 4; ++k) {
int nx = x + dx[k], ny = y + dy[k];
if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if (g[nx][ny] == '#') continue;
if (distM[nx][ny] > distM[x][y] + 1) {
distM[nx][ny] = distM[x][y] + 1;
qm.push(make_pair(nx, ny));
}
}
}
// 人的 BFS
qa.push(A);
distA[A.first][A.second] = 0;
// 若起點在邊界且安全
if (A.first == 0 || A.first == n-1 || A.second == 0 || A.second == m-1) {
if (0 < distM[A.first][A.second]) {
cout << "YES\n0\n\n";
return 0;
}
}
pair<int,int> exitCell(-1,-1);
while (!qa.empty()) {
pair<int,int> cur = qa.front(); qa.pop();
int x = cur.first, y = cur.second;
for (int k = 0; k < 4; k++) {
int nx = x + dx[k], ny = y + dy[k];
if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if (g[nx][ny] == '#') continue;
int nt = distA[x][y] + 1;
if (distA[nx][ny] != (int)1e9) continue;
if (nt >= distM[nx][ny]) continue; // 不安全
distA[nx][ny] = nt;
fromDir[nx][ny] = dir[k]; // 走到 (nx,ny) 用了這個方向
qa.push(make_pair(nx, ny));
if (nx == 0 || nx == n-1 || ny == 0 || ny == m-1) {
exitCell = make_pair(nx, ny);
while (!qa.empty()) qa.pop(); // 提前結束
break;
}
}
}
if (exitCell.first == -1) {
cout << "NO\n";
return 0;
}
// 回溯路徑:fromDir 記的是前進方向,往回走相反方向取父節點
string path;
int x = exitCell.first, y = exitCell.second;
while (!(x == A.first && y == A.second)) {
char c = fromDir[x][y];
path.push_back(c);
if (c == 'U') x += 1;
else if (c == 'D') x -= 1;
else if (c == 'L') y += 1;
else y -= 1; // 'R'
}
reverse(path.begin(), path.end());
cout << "YES\n" << (int)path.size() << "\n" << path << "\n";
return 0;
}
原題:
https://cses.fi/problemset/task/1671
題意:
給 n 節點、m 條非負權重有向邊,輸出從 1 到每個節點的最短距離。若不可達,常以極大值(例如 1e18)表示。
解題思路:
#include <bits/stdc++.h>
using namespace std;
struct Edge { int to; long long w; };
int main() {
int n, m;
cin >> n >> m;
vector<vector<Edge> > g(n + 1);
for (int i = 0; i < m; i++) {
int a, b; long long w;
cin >> a >> b >> w;
g[a].push_back(Edge{b, w});
}
const long long INF = (long long)1e18;
vector<long long> dist(n + 1, INF);
vector<char> vis(n + 1, 0);
dist[1] = 0;
typedef pair<long long,int> Node; // (dist, id)
priority_queue<Node, vector<Node>, greater<Node> > pq;
pq.push(Node(0, 1));
while (!pq.empty()) {
Node cur = pq.top(); pq.pop();
long long d = cur.first;
int u = cur.second;
if (vis[u]) continue;
vis[u] = 1;
if (d != dist[u]) continue;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i].to;
long long nd = d + g[u][i].w;
if (nd < dist[v]) {
dist[v] = nd;
pq.push(Node(nd, v));
}
}
}
for (int i = 1; i <= n; i++) {
if (i > 1) cout << ' ';
cout << dist[i];
}
cout << '\n';
return 0;
}
原題:
https://leetcode.com/problems/rotting-oranges/description/
題意:
0=空、1=新鮮橘子、2=腐爛橘子。每分鐘腐爛會傳給四鄰的新鮮橘子。求使所有新鮮橘子腐爛的最少分鐘數;若不可能,回傳 -1。
解題思路:
先把所有 2 同時入隊做多源 BFS;每層代表 1 分鐘。
紀錄新鮮橘子總數 fresh,BFS 過程中每次腐爛一顆就遞減;最後若 fresh > 0 則 -1,否則回傳最後時間。
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int n = (int)grid.size();
if (n == 0) return 0;
int m = (int)grid[0].size();
const int DX[4] = {-1, 1, 0, 0};
const int DY[4] = {0, 0, -1, 1};
queue<pair<int,int> > q;
int fresh = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++) {
if (grid[i][j] == 2) q.push(make_pair(i, j));
else if (grid[i][j] == 1) ++fresh;
}
if (fresh == 0) return 0;
int minutes = -1;
while (!q.empty()) {
int qs = (int)q.size();
minutes++;
while (qs--) {
pair<int,int> cur = q.front(); q.pop();
int x = cur.first, y = cur.second;
for (int k = 0; k < 4; k++) {
int nx = x + DX[k], ny = y + DY[k];
if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
if (grid[nx][ny] != 1) continue;
grid[nx][ny] = 2;
fresh--;
q.push(make_pair(nx, ny));
}
}
}
return (fresh == 0) ? max(0, minutes) : -1;
}
};
原題:
https://leetcode.com/problems/surrounded-regions/description/
題意:
給 X/O 棋盤,上下左右連通的一群 O 若沒有接觸到邊界,就把那群 O 全翻成 X;否則保留。回傳最終棋盤。
解題思路(BFS 寫法;DFS 亦可):
class Solution {
// 將邊界上的 'O' 入隊並標成 'E'
void push_if_O(vector<vector<char>>& board, int n, int m,
int x, int y, queue<pair<int,int>>& q) {
if (x >= 0 && x < n && y >= 0 && y < m && board[x][y] == 'O') {
board[x][y] = 'E';
q.push(make_pair(x, y));
}
}
public:
void solve(vector<vector<char>>& board) {
int n = (int)board.size();
if (n == 0) return;
int m = (int)board[0].size();
queue<pair<int,int>> q;
// 1) 從「邊界的 O」開始做 BFS(先標成 E,代表能逃出去)
for (int i = 0; i < n; ++i) {
push_if_O(board, n, m, i, 0, q);
push_if_O(board, n, m, i, m - 1, q);
}
for (int j = 0; j < m; ++j) {
push_if_O(board, n, m, 0, j, q);
push_if_O(board, n, m, n - 1, j, q);
}
// 2) 從這些 E 擴張,把所有與邊界連通的 O 標為 E
const int DX[4] = {-1, 1, 0, 0};
const int DY[4] = {0, 0, -1, 1};
while (!q.empty()) {
pair<int,int> cur = q.front(); q.pop();
int x = cur.first, y = cur.second;
for (int k = 0; k < 4; ++k) {
int nx = x + DX[k], ny = y + DY[k];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && board[nx][ny] == 'O') {
board[nx][ny] = 'E';
q.push(make_pair(nx, ny));
}
}
}
// 3) 統一翻轉:剩下的 O(被包圍)→ X;E(能逃)→ O
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j) {
if (board[i][j] == 'O') board[i][j] = 'X';
else if (board[i][j] == 'E') board[i][j] = 'O';
}
}
};