BFS

interviewsangu·2020년 7월 17일
0

알고리즘

목록 보기
6/12
post-thumbnail
  • BFS는 임의의 시작 점에서 모든 정점을 한번씩 방문하는 알고리즘이다.

  • 최단 거리를 구하는 알고리즘이기도 하다.

  • BFS를 이용해 해결할 수 있는 최단 거리 문제는 다음과 같은 조건들을 만족해야 한다.
    (1) 최소 비용 문제
    (2) 간선의 가중치는 모두 1
    (3) 정점과 간선의 개수가 적당히 적어야 한다.

  • 간선에 1이 아닌 가중치가 주어진 경우, 특정 간선의 사용 횟수 제한이 있는 경우 주의

https://www.acmicpc.net/problem/2178
미로 탐색

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int check[100][100];
int dist[100][100];
int dy[] = { 1, -1, 0, 0 };
int dx[] = { 0, 0, 1, -1 };
int n, m;
void bfs(int y, int x) {
	queue<pair<int, int>> q;
	check[y][x] = 0;
	dist[y][x] = 1;
	q.push(make_pair(y, x));
	while (!q.empty()) {
		pair<int, int> now = q.front();
		q.pop();
		for (int i = 0; i < 4; i++) {
			int ny = now.first + dy[i];
			int nx = now.second + dx[i];
			if (nx < 0 || ny < 0 || nx >= m || ny >= n) continue;
			if (check[ny][nx] == 1) {
				q.push(make_pair(ny, nx));
				check[ny][nx] = 0;
				dist[ny][nx] = dist[now.first][now.second] + 1;
			}
		}
	}
}
int main() {
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			scanf("%1d", &check[i][j]);
		}
	}
	bfs(0, 0);
	cout << dist[n - 1][m - 1] << '\n';
	return 0;
}

https://www.acmicpc.net/problem/7576
토마토
시작점이 여러개인 경우 모두 큐에 쳐 넣으면 됨, 알아서 여러 시작점에서 순차적으로 시작

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int check[1000][1000];
int dist[1000][1000];
int dy[] = { 1, -1, 0, 0 };
int dx[] = { 0, 0, 1, -1 };
int main() {
	int n, m;
	cin >> m >> n;
	queue<pair<int, int>> q;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> check[i][j];
			if (check[i][j] == 1) {
				q.push(make_pair(i, j));
				dist[i][j] = 0;
			}
		}
	}
	while (!q.empty()) {
		pair<int, int> now = q.front();
		q.pop();
		for (int i = 0; i < 4; i++) {
			int ny = now.first + dy[i];
			int nx = now.second + dx[i];
			if (nx < 0 || ny < 0 || nx >= m || ny >= n) continue;
			if (check[ny][nx] == 0) {
				q.push(make_pair(ny, nx));
				check[ny][nx] = 1;
				dist[ny][nx] = dist[now.first][now.second] + 1;
			}
		}
	}
	bool zero = false;
	int ans = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (ans < dist[i][j]) ans = dist[i][j];
			if (check[i][j] == 0) zero = true;
		}
	}
	if (zero) cout << -1 << '\n';
	else cout << ans << '\n';
	return 0;
}

https://www.acmicpc.net/problem/1697
숨바꼭질

런타임 에러 나온 이유 : next 범위 확인 안함, 끝자리 확인 주의

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int sec[100001];
int main() {
	memset(sec, -1, sizeof(sec));
	int n, k;
	cin >> n >> k;
	if (n == k) {
		cout << 0 << '\n';
		return 0;
	}
	queue<int> q;
	sec[n] = 0;
	q.push(n);
	while (!q.empty()) {
		int now = q.front();
		if (now == k) break;
		q.pop();
		int next;
		next = now + 1;
		if (next <100001 && sec[next] == -1) {
			sec[next] = sec[now] + 1;
			q.push(next);
		}
		next = now - 1;
		if (next >= 0 && sec[next] == -1) {
			sec[next] = sec[now] + 1;
			q.push(next);
		}
		next = now * 2;
		if (next < 100001 && sec[next] == -1) {
			sec[next] = sec[now] + 1;
			q.push(next);
		}
	}
	cout << sec[k] << '\n';
	return 0;
}

https://www.acmicpc.net/problem/14226
이모티콘

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int sec[1001][1001];
int main() {
	memset(sec, -1, sizeof(sec));
	int s;
	cin >> s;
	queue<pair<int, int>> q;
	sec[1][0] = 0;
	q.push(make_pair(1, 0));
	while (!q.empty()) {
		int now = q.front().first;
		if (now == s) break;
		int copy = q.front().second;
		q.pop();
		if (sec[now][now] == -1) {
			sec[now][now] = sec[now][copy] + 1;
			q.push(make_pair(now, now));
		}
		if (sec[now + copy][copy] == -1 && copy != 0
			&& now + copy <= 1000) {
			sec[now + copy][copy] = sec[now][copy] + 1;
			q.push(make_pair(now + copy, copy));
		}
		if (sec[now - 1][copy] == -1 && now - 1 >= 0) {
			sec[now - 1][copy] = sec[now][copy] + 1;
			q.push(make_pair(now - 1, copy));
		}
	}
	cout << sec[q.front().first][q.front().second] << '\n';
	return 0;
}

덱 사용하기

https://www.acmicpc.net/problem/13549
숨바꼭질 3
시간초가 적은 것들이 먼저 큐에 들어가게 해야 한다.
&& 연산에서 범위가 먼저 검색되게 해야 한다

큐로 풀 때 순서에 의해서 바뀐다고??? -> 그냥 덱으로 풀자
n = 1일 때 어디로 순서가 바뀔 수 있어서 그런가?

#include <iostream>
#include <queue>
#include <deque>
using namespace std;
bool c[1000000];
int d[1000000];
int MAX = 1000000;
int main() {
    int n, m;
    cin >> n >> m;
    c[n] = true;
    d[n] = 0;
    deque<int> q;
    q.push_back(n);
    while (!q.empty()) {
        int now = q.front();
        q.pop_front();
        if (now+1 < MAX) {
            if (c[now+1] == false && (now + 1) != 2) {
                q.push_back(now+1);
                c[now+1] = true;
                d[now+1] = d[now] + 1;
            }
        }
        if (now*2 < MAX) {
            if (c[now*2] == false) {
                q.push_front(now*2);
                c[now*2] = true;
                d[now*2] = d[now];
            }
        }
        if (now-1 >= 0) {
            if (c[now-1] == false) {
                q.push_back(now-1);
                c[now-1] = true;
                d[now-1] = d[now] + 1;
            }
        }
    }
    cout << d[m] << '\n';
    return 0;
}

https://www.acmicpc.net/problem/1261
알고스팟

벽을 최소로 뚫고 나가야하기 때문에 벽을 깨부순 개수로 나눠야 한다
큐를 새로 만들거나 덱을 사용한다.

#include <cstdio>
#include <queue>
using namespace std;
int d[555][555];
int a[555][555];
int n,m;
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
int main() {
    scanf("%d %d",&m,&n);
    for (int i=0; i<n; i++) {
        for (int j=0; j<m; j++) {
            scanf("%1d",&a[i][j]);
            d[i][j] = -1;
        }
    }
    queue<pair<int,int>> q;
    queue<pair<int,int>> next_queue;
    q.push(make_pair(0,0));
    d[0][0] = 0;
    while (!q.empty()) {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();
        for (int k=0; k<4; k++) {
            int nx = x+dx[k];
            int ny = y+dy[k];
            if (0 <= nx && nx < n && 0 <= ny && ny < m) {
                if (d[nx][ny] == -1) {
                    if (a[nx][ny] == 0) {
                        d[nx][ny] = d[x][y];
                        q.push(make_pair(nx,ny));
                    } else {
                        d[nx][ny] = d[x][y]+1;
                        next_queue.push(make_pair(nx,ny));
                    }
                }
            }
        }
        if (q.empty()) {
            q = next_queue;
            next_queue = queue<pair<int,int>>();
        }
    }
    printf("%d\n",d[n-1][m-1]);
    return 0;
}
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
int room[101][101];
int cnt[101][101];
int dx[] = { 0, 0, 1, -1 };
int dy[] = { 1, -1, 0, 0 };
int main() {
	int m, n;
	cin >> m >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			scanf("%1d", &room[i][j]);
		}
	}
	deque<pair<int, int>> q;
	cnt[1][1] = 0;
	room[1][1] = -1;
	q.push_back(make_pair(1, 1));
	while (!q.empty()) {
		int y = q.front().first;
		int x = q.front().second;
		q.pop_front();
		for (int i = 0; i < 4; i++) {
			int ny = y + dy[i];
			int nx = x + dx[i];
			if (ny <= 0 || nx <= 0 || ny > n || nx > m) continue;
			if (room[ny][nx] == 0) {
				room[ny][nx] = -1;
				cnt[ny][nx] = cnt[y][x];
				q.push_front(make_pair(ny, nx));
			}
			if (room[ny][nx] == 1) {
				room[ny][nx] = -1;
				cnt[ny][nx] = cnt[y][x] + 1;
				q.push_back(make_pair(ny, nx));
			}
		}
	}
	cout << cnt[n][m] << '\n';
	return 0;
}

https://www.acmicpc.net/problem/2206
벽 부수고 이동하기

이전 문제가 벽을 부수는 개수에 따라 큐를 계속해서 만들어 사용한다면 (예를 들어 벽 0개 큐를 모두 소모하고 벽 1개로 넘어가고 ..) 이 문제는 딱 한번만 부술 수 있다.

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <tuple>
using namespace std;
int room[1001][1001];
int dist[1001][1001][2];
int dx[] = { 0, 0, 1, -1 };
int dy[] = { 1, -1, 0, 0 };
int main() {
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			scanf("%1d", &room[i][j]);
		}
	}
	queue<tuple<int, int, int>> q;
	dist[1][1][0] = 1;
	q.push(make_tuple(1, 1, 0));
	while (!q.empty()) {
		int y, x, z;
		tie(y, x, z) = q.front();
		q.pop();
		for (int i = 0; i < 4; i++) {
			int ny = y + dy[i];
			int nx = x + dx[i];
			if (nx <= 0 || ny <= 0 || nx > m || ny > n) continue;
			if (room[ny][nx] == 0 && dist[ny][nx][z] == 0) {
				dist[ny][nx][z] = dist[y][x][z] + 1;
				q.push(make_tuple(ny, nx, z));
			}
			if (z == 0 && room[ny][nx] == 1 && dist[ny][nx][z + 1] == 0) {
				dist[ny][nx][z + 1] = dist[y][x][z] + 1;
				q.push(make_tuple(ny, nx, z + 1));
			}
		}
	}
	if (dist[n][m][0] != 0 && dist[n][m][1] != 0) {
		cout << min(dist[n][m][0], dist[n][m][1]);
	}
	else if (dist[n][m][0] != 0) {
		cout << dist[n][m][0];
	}
	else if (dist[n][m][1] != 0) {
		cout << dist[n][m][1];
	}
	else {
		cout << -1;
	}
	cout << '\n';
	return 0;
}

룸을 바꾸면 안됨, z = 0에서 변경하면 z = 1에서 사용 못함

https://www.acmicpc.net/problem/3055
탈출

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <tuple>
#include <cstring>
using namespace std;
int water[50][50];
int go[50][50]; 
int dx[] = { 0, 0, 1, -1 };
int dy[] = { 1, -1, 0, 0 };
int main() {
	int r, c;
	cin >> r >> c;
	vector<string> a(r);
	for (int i = 0; i < r; i++) {
		cin >> a[i];
	}
	memset(water, -1, sizeof(water));
	queue<pair<int, int>> q;
	int gx, gy, ex, ey;
	for (int i = 0; i < r; i++) {
		for (int j = 0; j < c; j++) {
			if (a[i][j] == '*') {
				q.push(make_pair(i, j));
				water[i][j] = 0;
			}
			else if (a[i][j] == 'S') {
				gx = i;
				gy = j;
			}
			else if (a[i][j] == 'D') {
				ex = i;
				ey = j;
			}
		}
	}
	while (!q.empty()) {
		int x, y;
		tie(x, y) = q.front();
		q.pop();
		for (int i = 0; i < 4; i++) {
			int nx = x + dx[i];
			int ny = y + dy[i];
			if (nx < 0 || nx >= r || ny < 0 || ny >= c) continue;
			if (water[nx][ny] == -1 && a[nx][ny] == '.') {
				water[nx][ny] = water[x][y] + 1;
				q.push(make_pair(nx, ny));
			}
		}
	}
	memset(go, -1, sizeof(go));
	go[gx][gy] = 0;
	q.push(make_pair(gx, gy));
	while (!q.empty()) {
		int x, y;
		tie(x, y) = q.front();
		q.pop();
		for (int i = 0; i < 4; i++) {
			int nx = x + dx[i];
			int ny = y + dy[i];
			if (nx < 0 || nx >= r || ny < 0 || ny >= c) continue;
			if (go[nx][ny] != -1) continue;
			if (a[nx][ny] == 'X') continue;
			if (water[nx][ny] == -1 || go[x][y] + 1 < water[nx][ny]) {
				go[nx][ny] = go[x][y] + 1;
				q.push(make_pair(nx, ny));
			}
		}
	}
	if (go[ex][ey] == -1) {
		cout << "KAKTUS\n";
	}
	else {
		cout << go[ex][ey] << '\n';
	}
	return 0;
}

0개의 댓글