문제 링크


https://www.acmicpc.net/problem/14502

 

14502번: 연구소

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크�

www.acmicpc.net

접근 방법


 위 문제는 벽 3개를 세울 수 있는 모든 경우에 대해 벽을 세운 후 바이러스를 퍼뜨려 안전영역의 최댓값을 구하면 된다. 

 

 벽을 3개를 세우는 방법은 백트래킹으로 가능하나 for문을 3중으로 사용하는 것도 가능하다. 필자는 3중 for문을 사용했으며, 취향에 따라 방법을 달리하여도 된다. 벽을 3개를 세운 뒤 바이러스를 퍼뜨리는 방법은 BFS를 사용하면 된다.

 

1. 일단 main()에서 입력을 받는 동시에 빈 공간의 위치와 바이러스의 위치의 정보를 저장해 두었다.

for (int i = 0; i < n; i++) 
	for (int j = 0; j < m; j++) {
    cin >> room[i][j];
    	if (room[i][j] == 0)
    		zeros.push_back({ i,j }); //빈공간
    	else if (room[i][j] == 2)
    		virus.push_back({ i,j }); //바이러스 초기위치
    }

2. 3중 for문을 사용하여 벽 3개를 세울 위치를 각각 first, second, third로 받았으며 해당 위치를 map에서 1로 변경 후 bfs를 사용하여 바이러스를 퍼뜨려 바이러스가 퍼진 공간을 cnt로 반환받았으며 안전영역의 수는 아래와 같이 구할 수 있다.

int safe = zeros.size() - 3 - cnt;

safe(바이러스가 퍼진 후 안전영역) =

zeros.size()(바이러스가 퍼지기 이전의 안전영역 수) - 3(벽을 새로 세운 공간) - cnt(바이러스가 퍼진 영역)

 

 

 

소스 코드


#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <string.h>

using namespace std;

int room[8][8];
vector <pair<int, int>> zeros;
vector <pair<int, int>> virus;
pair<int, int> direction[4] = { {1,0}, {-1,0}, {0,1}, {0,-1} };
int n, m;
int max_ = 0;

int bfs(int x, int y) {

	int cnt = 0;
	queue < pair<int, int>> q;
	q.push({ x,y });
	while (!q.empty()) {
		auto elem = q.front();
		int i = elem.first;
		int j = elem.second;
		q.pop();
		for (int k = 0; k < 4; k++) {
			int next_x = i + direction[k].first;
			int next_y = j + direction[k].second;
			if (0 <= next_x && next_x < n && 0 <= next_y && next_y < m && room[next_x][next_y] == 0) {
				q.push({ next_x, next_y });
				room[next_x][next_y] = 2;
				cnt++;
			}
		}
	}
	return cnt;
}

void build_wall(int n, int m) {

	for (int i = 0; i < zeros.size(); i++) {
		for (int j = i + 1; j < zeros.size(); j++) {
			for (int k = j + 1; k < zeros.size(); k++) {
				int cnt = 0;
				auto first = zeros[i];
				auto second = zeros[j];
				auto third = zeros[k];
				room[first.first][first.second] = 1;
				room[second.first][second.second] = 1;
				room[third.first][third.second] = 1;
				for (auto &elem : virus) {
					cnt += bfs(elem.first, elem.second); //바이러스가 퍼진 영역의 수
				}
				int safe = zeros.size() - 3 - cnt;
				max_ = max(max_, safe);
				for (auto &elem : zeros) {
					room[elem.first][elem.second] = 0;
				}
				room[first.first][first.second] = 0;
				room[second.first][second.second] = 0;
				room[third.first][third.second] = 0;
			}
		}
	}
}

int main() {

	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	memset(room, -1, sizeof(room));

	cin >> n >> m;
	for (int i = 0; i < n; i++) 
		for (int j = 0; j < m; j++) {
			cin >> room[i][j];
			if (room[i][j] == 0)
				zeros.push_back({ i,j }); //빈공간
			else if (room[i][j] == 2)
				virus.push_back({ i,j }); //바이러스 초기위치
		}
	build_wall(n, m);
	cout << max_ << "\n";
}

개발 환경 : VS 2019

질문, 지적 환영입니다.

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 2580 스도쿠  (0) 2020.06.10
[C++] 백준 4195 친구네트워크  (0) 2020.06.09
[C++] 백준 17779 게리맨더링2  (0) 2020.05.29
[C++] 백준 17837 새로운 게임 2  (0) 2020.05.29
[C++] 백준 1520 내리막길  (0) 2020.05.26

문제 링크


https://www.acmicpc.net/problem/1520

 

1520번: 내리막 길

여행을 떠난 세준이는 지도를 하나 구하였다. 이 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 한 칸은 한 지점을 나타내는데 각 칸에는 그 지점의 높이가 쓰여 있으�

www.acmicpc.net

 

접근 방법


 

 이 문제는 내리막 길(현재보다 더 값이 작은 지점)으로만 따라 갔을 때, 목적지에 도착하는 길의 수를 구하는 문제이며, 문제를 보았을때 DFS를 사용하면 top-down방식으로 가능할 것이고, BFS를 사용하면 bottom-up방식이 가능하다. 필자는 DFS를 이용한 top-down방식으로 해결하였다.

 

(1) 변수

 dp[i][j]는 i,j에서 출발하였을 때, 내리막 길을 따라 도착지에 도착하였을 경우에 대한 길의 수이며, 방문 여부로도 사용된다. 초기값을 0으로 사용하게 되면, 방문을 하였어도 도착지점까지 못 간다면 계속 0이기 때문에 방문여부를 확인하기 어렵기 때문에 초기값을 -1로 하였다.

 

(2) solve()

int &ret = dp[x][y];
if (ret != -1) return ret;
if (x == n && y == m) return ret = 1;
ret = 0;

 함수가 호출되었을때, 가장 먼저 방문여부( if (ret != -1) )를 판단한다. 중복되는 연산을 피하기 위해서이다. 방문을 하였으면 해당 dp에 저장된 값을 return한다. 좀 더 자세히 설명을 하자면 어차피 현재 함수의 x,y에서 갈 수 있는 경로를 이미 다 탐색을 한 값이 dp[x][y]에 저장되어 있으므로 함수를 더 진행시킬 필요가 없다는 것이다.   

 그 다음 도착지점에 도착했다면( if (x == n && y == m) ), 도착한 경로에 대한 경우가 추가 되었으므로 1을 return한다.

 위의 두가지 경우가 아니면 방문을 했다는 의미로 값을 0으로 바꿔준다.

 

for (int i = 0; i < 4; i++) {
		int next_x = x + direction[i].first;
		int next_y = y + direction[i].second;
		if (1 <= next_x && next_x <= n && 1 <= next_y && next_y <= m) 
			if (map[next_x][next_y] < map[x][y]) 
				ret += solve(next_x, next_y);
}

그 이후 현재좌표의 상하좌우 4방향에 대해 map에 포함되어 있는 범위 내에서, 값이 더 작은 곳으로 이동시키면 된다.

소스 코드


#include <iostream>
#include <string.h>

using namespace std;
int n, m;
int map[501][501];
int dp[501][501];
pair<int, int> direction[4] = { {1,0}, {0,1}, {-1,0}, {0,-1} };

int solve(int x, int y) {
    
	int &ret = dp[x][y];
	if (ret != -1) return ret;
	if (x == n && y == m) return ret = 1;
	ret = 0;
    
	for (int i = 0; i < 4; i++) {
		int next_x = x + direction[i].first;
		int next_y = y + direction[i].second;
		if (1 <= next_x && next_x <= n && 1 <= next_y && next_y <= m) 
			if (map[next_x][next_y] < map[x][y]) 
				ret += solve(next_x, next_y);
	}
	return ret;
}

int main() {
    
	ios_base::sync_with_stdio(NULL);
	cin.tie(0);
	cout.tie(0);
    
	memset(dp, -1, sizeof(dp));
	cin >> n >> m;
    
	for (int i = 1; i <= n; i++) 
		for (int j = 1; j <= m; j++) 
			cin >> map[i][j];
	cout << solve(1, 1);
}

개발 환경 : VS 2019

질문, 지적 환영입니다.

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 17779 게리맨더링2  (0) 2020.05.29
[C++] 백준 17837 새로운 게임 2  (0) 2020.05.29
[C++] 백준 5373 큐빙  (0) 2020.05.26
[C++] 백준 13460 구슬 탈출 2  (0) 2020.05.26
[C++] 백준 15684 사다리 조작  (0) 2020.05.25

문제 링크


https://www.acmicpc.net/problem/15684

 

15684번: 사다리 조작

사다리 게임은 N개의 세로선과 M개의 가로선으로 이루어져 있다. 인접한 세로선 사이에는 가로선을 놓을 수 있는데, 각각의 세로선마다 가로선을 놓을 수 있는 위치의 개수는 H이고, 모든 세로선

www.acmicpc.net

 

접근 방법


 이 문제는 사다리에 추가적으로 가로선을 0개 부터 최대 3개까지 놓는 가능한 모든 경우의 수를 다 보아야 해결 할 수 있다.  결국 조합 문제로 해석될 수 있으므로, 백트래킹을 하며 모든 조합에 대해 문제의 조건은 1번째부터 i번째에서 출발할 때 순서대로 1번째부터 i번째의 도착 지점에 도달하면 카운트하고, 그 카운트가 4이상이거나 가능한 방법이 없다면 -1을 출력하면 된다.

 

(1) 변수

map[h][l]는 h의 높이, l번째 라인이 h의 높이, l+1번째 라인에 가로선이 연결되어 있다면 1, 아니면 0으로 나타낸다.  

 

(2) go() 함수

bool go() {
	for (int i = 1; i <= N; i++) {
		int nowl = i; 
		for (int nowh = 0; nowh <= H; nowh++){
			if (map[nowh][nowl] == 1) 
				nowl++;
			else if (map[nowh][nowl - 1] == 1) 
				nowl--;
		}
		if (nowl != i)
			return false;
	}
	return true;
}

이 함수는 사다리의 출발지점과 도착지점이 같은지 따라가보는 함수이다.

 모든 사다리의 출발지점 1~N에 대해 확인 하여야 하기 때문에 for문에서 경우를 검사해보자. 현재 출발 라인을 nowl = i 라 하고, nowh(0~H)를 따라갈때, 우측에 사다리가 있는 경우 (if(map[nowh][nowl] == 1))   라인을 오른쪽으로 옮겨갈 것(nowl++)이고, 좌측에 사다리가 있는 경우 (if(map[nowh][nowl - 1] == 1))   라인을 왼쪽으로 옮겨갈 것(nowl--)이다.

 

(3) solve()함수

void solve(int cnt, int now_h, int now_l) {
	if (go()) {
		result = min(cnt, result);
		return;
	}
	if (cnt == 3) 
		return;

	for (int h = now_h; h <= H; h++) {
		for (int l = now_l; l < N; l++) {
			if (map[h][l] == 0 && map[h][l - 1] == 0 && map[h][l + 1] == 0) {
				map[h][l] = 1;
				solve(cnt + 1, h, l);
				map[h][l] = 0;
			}
		}
		now_l = 1;
	}
}

이 함수는 조합을 보기 위한 백트래킹(DFS) 함수이다. 중복되는 경우를 보지 않기 위해 매게변수로(nowl, nowh)를 사용하였으며, 위 함수가 호출되는 모든 경우에 대해 위에 설명한 go()함수로 조건을 만족하는지 검사한다. cnt가 3보다 커지는 경우는 고려하지 않으므로 cnt는 3에서 return을 하게 된다.

 

 가로선을 추가 할 수 있는 경우인,

양쪽에 가로라인이 존재하지 않을 때(if (map[h][l] == 0 && map[h][l - 1] == 0 && map[h][l + 1] == 0))

가로라인을 추가(map[h][l] = 1)하고, 함수를 진행 시킨뒤(solve(cnt + 1, h, 1)), 함수가 종료되면 해당 가로라인을 다시 삭제 (map[h][l] = 0)하면 된다. 

 

소스 코드


#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int N, M, H;
int map[32][12]; //height, line
int result = 4;	

bool go() {
	
	for (int i = 1; i <= N; i++) {
		int nowl = i; 
		for (int nowh = 0; nowh <= H; nowh++){
			if (map[nowh][nowl] == 1) 
				nowl++;
			else if (map[nowh][nowl - 1] == 1) 
				nowl--;
		}
		if (nowl != i)
			return false;
	}
	return true;
}

void solve(int cnt, int now_h, int now_l) {
	if (go()) {
		result = min(cnt, result);
		return;
	}
	if (cnt == 3) 
		return;

	for (int h = now_h; h <= H; h++) {
		for (int l = now_l; l < N; l++) {
			if (map[h][l] == 0 && map[h][l - 1] == 0 && map[h][l + 1] == 0) {
				map[h][l] = 1;
				solve(cnt + 1, h, l);
				map[h][l] = 0;
			}
		}
		now_l = 1;
	}
}

int main() {

	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> N >> M >> H;
	for (int i = 1; i <= M; i++) {
		int a, b;
		cin >> a >> b; //a높이 b라인
		map[a][b] = 1;
	}
	solve(0, 1, 1);
	
	if (result >= 4)
		cout << -1 << "\n";
	else
		cout << result << "\n";
}

개발 환경 : VS 2019

질문, 지적 환영입니다.

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 5373 큐빙  (0) 2020.05.26
[C++] 백준 13460 구슬 탈출 2  (0) 2020.05.26
[C++] 백준 14891 톱니바퀴  (0) 2020.05.25
[C++} 백준 14890 경사로  (0) 2020.05.25
[C++] 백준 15683 감시  (0) 2020.05.20

문제 링크


https://www.acmicpc.net/problem/12865

 

12865번: 평범한 배낭

첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)

www.acmicpc.net

 

 

 

접근 방법


 

이 문제는 knapsack 알고리즘의 가장 기본적인 문제이다.

 

(1) 변수

    dp[i]는 배낭의 가용한 최대 무게가 i일때 담을 수 있는 최대의 가치를 의미한다.

    tmp[i]는dp[i]를 갱신하기 전에 미리 복사를 해놓는 용도로 사용. (dp[i]는 실시간으로 갱신 되기 때문에 갱신된 값이 사용되는 것을 방지하고, dp를 2차원으로 사용하는 것보다 시간적 메모리적 효율이 좋다.) 

 

(2) solve() 함수

if (item[i].first <= j) {
	dp[j] = max(tmp[j], tmp[j - item[i].first] + item[i].second);
}

    가장 핵심이 되는 부분인데, max()안에서 tmp[j]는 현제 가리키고 있는 물건 item[i]를 담지 않았을 때의 경우이고,

tmp[j - item[i].first] + item[i].second는 item[i]를 담는 경우, item[i]의 무게를 제외한 부분의 가용한 무게에서의 가치의 최댓값 tmp[j - item[i].first]에 현재 item[i]의 가치를 담는 것이다. 그 둘의 값중 더 큰 가치를 가지는 것으로 dp[j]를

갱신하면 된다.

    

소스 코드


#include <vector>
#include <algorithm>

using namespace std;
pair <int, int> item[101];
int dp[100001]; 
int tmp[100001];
int n, k;

int solve() {
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= k; j++) {
			tmp[j] = dp[j];
		}
		for (int j = 1; j <= k; j++) {
			if (item[i].first <= j) {
				dp[j] = max(tmp[j], tmp[j - item[i].first] + item[i].second);
			}
		}
	}
	return dp[k];
}

int main() {
	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		int w, v;
		cin >> w >> v;
		item[i] = { w,v }; //{무게, 가치}
	}
	cout << solve();
}

개발 환경 : VS 2019

질문, 지적 환영입니다.

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 1697 숨바꼭질  (0) 2020.05.19
[C++] 백준 14501 퇴사  (0) 2020.05.19
[C++] 백준 15686 치킨 배달  (0) 2020.05.18
[C++] 백준 16236 아기 상어  (0) 2020.05.18
[C++] 백준 14499 주사위 굴리기  (0) 2020.05.18

+ Recent posts