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

 

2146번: 다리 만들기

여러 섬으로 이루어진 나라가 있다. 이 나라의 대통령은, 섬들을 잇는 다리를 만들겠다는 공약으로 인기몰이를 해 당선될 수 있었다. 하지만 막상 대통령에 취임하자, 다리를 놓는다는 것이 아깝다는 생각을 하게 되었다. 그래서 그는, 생색내는 식으로 한 섬과 다른 섬을 잇는 다리 하나만을 만들기로 하였고, 그 또한 다리를 가장 짧게 하여 돈을 아끼려 하였다. 이 나라는 N×N크기의 이차원 평면상에 존재한다. 이 나라는 여러 섬으로 이루어져 있으며, 섬이란 동서

www.acmicpc.net

 

dfs와 bfs를 모두 활용한다.

 

dfs를 통해서 섬의 개수를 파악하고, 섬마다 index를 할당해준다.

 

이후에는 섬으로부터 바다로 탐색을 수행하고, 이때 bfs를 활용한다.

 

거리를 측정하기 시작한 섬에 대한 정보를 idx 배열에 저장해준다.

 

즉 i행 j열이 바다라고 한다면, 섬이 아니기 때문에 index가 할당되어있지 않은데, 

 

어느 섬으로부터 측정한 거리라는 것을 idx 배열에 명시해준다.

 

이를 통해서 다른섬이 만났을 때, 그 지점까지의 거리가 만들 수 있는 다리의 최소 거리인지 판단해준다.

 

#include<iostream>
#include<queue>
using namespace std;
int m[100][100], n, dis[100][100], idx[100][100];
int dr[4] = { 0,0,1,-1 };
int dc[4] = { 1,-1,0,0 };
int Min = -2;

queue <pair<int, int> > q;
void init() {
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			dis[i][j] = -1;
	while (!q.empty()) q.pop();
}
void minCal(int val) { //최소 거리인지 판단
	if (Min == -2) { //최솟값이 갱신된 적이 없다면
		Min = val;
		return;
	}
	else {
		if (Min > val) Min = val;
		else
			return;
	}
}
bool boundCheck(int r, int c) {
	return r < 0 || c < 0 || r >= n || c >= n;
}
void dfs(int row, int col, int landCnt) {
	dis[row][col]++;
	idx[row][col] = landCnt; //섬의 index 지정
	for (int i = 0; i < 4; i++) {
		int nr = row + dr[i];
		int nc = col + dc[i];
		if (boundCheck(nr, nc) || m[nr][nc] == 0 || dis[nr][nc] >= 0) continue;
		dfs(nr, nc, landCnt);
	}
}
void bfs(pair<int, int> start) {
	q.push(start);
	dis[start.first][start.second]++;

	while (!q.empty()) {
		pair<int, int> cur = q.front();
		q.pop();
		for (int i = 0; i < 4; i++) {
			int nr = cur.first + dr[i];
			int nc = cur.second + dc[i];
			if (boundCheck(nr, nc) || dis[nr][nc] >= 0) continue;
			if (m[nr][nc] == 1) {
				//섬을 만났을 때
				if (idx[nr][nc] == idx[cur.first][cur.second]) continue;
				else {
					//출발한 섬과 다른 섬을 만났을 때
					minCal(dis[cur.first][cur.second]);
					init();
					return;
				}
			}
			q.push({ nr, nc });
			dis[nr][nc] = dis[cur.first][cur.second] + 1;
			idx[nr][nc] = idx[cur.first][cur.second];
			//어느 섬에서 측정한 거리라는 것을 명시
		}
	}
}
int main() {
	cin >> n;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> m[i][j];
	init();
	
	int landCnt = 0; // 섬 번호
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (dis[i][j] < 0 && m[i][j] == 1) {
				landCnt++;
				dfs(i, j, landCnt);
			}
		}
	}
	init(); //방문처리 배열 초기화

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (dis[i][j] < 0 && m[i][j] == 1) {
				bfs({ i, j });
			}
		}
	}
	cout << Min << '\n';
	return 0;
}
//섬과 섬을 잇는 가장 짧은 다리
//1에서 시작해서 0으로 퍼져나가게
//퍼져나가기 시작한 점이 어느 섬인지 확인하면서 비교

//dfs로 섬 개수 파악하고 번호 매김
//섬이면서 방문 안 한 곳 bfs -> 주변이 바다인 곳 방문(거리측정)
//가다가 섬이면서 내 섬이 아닌 곳을 만나면 중지 -> 길이 최소 비교

 

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

 

1799번: 비숍

첫째 줄에 체스판의 크기가 주어진다. 체스판의 크기는 10이하의 자연수이다. 둘째 줄부터 아래의 예와 같이 체스판의 각 칸에 비숍을 놓을 수 있는지 없는지에 대한 정보가 체스판 한 줄 단위로 한 줄씩 주어진다. 비숍을 놓을 수 있는 곳에는 1, 비숍을 놓을 수 없는 곳에는 0이 빈칸을 사이에 두고 주어진다.

www.acmicpc.net

흑과 백으로 칸을 구분한다.

 

1을 흑 0을 백이라고 하면

 

1 0 1 0

0 1 0 1

1 0 1 0

0 1 0 1

 

이런식으로 체스판이 이루어져있다고 생각하고 흑은 흑대로 먼저 처리하고, 백은 백대로 먼저 처리하는 것이다.

 

개인적으로 굉장히 해내기 어려운 발상이라고 생각한다.

 

#include<iostream>
using namespace std;
int n, m[10][10];
int cnt = 0, Bmax = 0, Wmax = 0;
bool isused1[20], isused2[20];
void Black(int row, int col, int cnt) {
	if (col >= n) {
		row++;
		if (row % 2 == 0) col = 0;
		else
			col = 1;
	}
	if (Bmax < cnt) Bmax = cnt;
	if (row >= n) return;
	
	if (m[row][col] == 1 && !isused1[row + col]
		&& !isused2[row - col + n - 1]) {
		isused1[row + col] = true;
		isused2[row - col + n - 1] = true;
		Black(row, col + 2, cnt + 1); //(r,c)가 가능한 지점일 때
		isused1[row + col] = false;
		isused2[row - col + n - 1] = false;
	}
	Black(row, col + 2, cnt);
}
void White(int row, int col, int cnt) {
	if (col >= n) {
		row++;
		if (row % 2 == 0) col = 1;
		else
			col = 0;
	}
	if (Wmax < cnt) Wmax = cnt;
	if (row >= n) return;

	if (m[row][col] == 1 && !isused1[row + col]
		&& !isused2[row - col + n - 1]) {
		isused1[row + col] = true;
		isused2[row - col + n - 1] = true;
		White(row, col + 2, cnt + 1); //(r,c)가 가능한 지점일 때
		isused1[row + col] = false;
		isused2[row - col + n - 1] = false;
	}
	White(row, col + 2, cnt);
}
int main() {
	cin >> n;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> m[i][j];
	

	Black(0, 0, 0);
	for (int i = 0; i < 2 * n - 1; i++) {
		isused1[i] = false;
		isused2[i] = false;
	}
	White(0, 1, 0);
	cout << Wmax + Bmax << '\n';
}

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

 

1799번: 비숍

첫째 줄에 체스판의 크기가 주어진다. 체스판의 크기는 10이하의 자연수이다. 둘째 줄부터 아래의 예와 같이 체스판의 각 칸에 비숍을 놓을 수 있는지 없는지에 대한 정보가 체스판 한 줄 단위로 한 줄씩 주어진다. 비숍을 놓을 수 있는 곳에는 1, 비숍을 놓을 수 없는 곳에는 0이 빈칸을 사이에 두고 주어진다.

www.acmicpc.net

 

이 문제를 무작정 n^2으로 풀면 TLE를 받는다.

 

아래는 시간초과가 나는 코드이다.

 

한 대각선에 비숍은 하나밖에 존재할 수 없다는 것을 이용하면 복잡도를 2n-1 즉 n으로 떨어트릴 수 있을 것이다.

 

#include<iostream>
using namespace std;
int n, m[10][10];
int cnt = 0, Max = 0;
bool isused1[10], isused2[10];
void func(int k) {
	
	if (Max < k) Max = k;

	bool isExist1 = false , isExist2 = false;
	for (int i = 0; i < 10; i++) {
		if (!isused1[i]) {
			isExist1 = true;
			break;
		}
		if (!isused2[i]) {
			isExist2 = true;
			break;
		}
	}

	if (!isExist1 && !isExist2) return;

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (m[i][j] == 1 && !isused1[i + j] &&
				!isused2[i - j + n - 1]) {
				//cnt++;
				isused1[i + j] = true;
				isused2[i - j + n - 1] = true;
				func(k + 1);
				isused1[i + j] = false;
				isused2[i - j + n - 1] = false;
			}
		}
	}
	return;
}
int main() {
	cin >> n;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> m[i][j];
	
	func(0);
	cout << Max << '\n';
}

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

 

1759번: 암호 만들기

첫째 줄에 두 정수 L, C가 주어진다. (3 ≤ L ≤ C ≤ 15) 다음 줄에는 C개의 문자들이 공백으로 구분되어 주어진다. 주어지는 문자들은 알파벳 소문자이며, 중복되는 것은 없다.

www.acmicpc.net

 

C개의 수를 입력받아서 길이 L의 수열을 출력하면 된다.

 

입력은 소문자로만 들어오고, 정렬되어서 들어오지 않는다.

 

출력되는 수열은 오름차순 정렬 상태여야 하기 때문에 입력을 받은 이후에 정렬이 필요하다. 그리고 사전순의 상태를 유지하기 위해서 st라는 변수를 두어서 이전에 사용된 문자의 인덱스를 관리한다.

 

문자의 길이가 0이 되면 이 전에 사용한 문자의 인덱스가 존재하지 않기 때문에 0으로 처리해준다.

 

문자를 중복해서 사용할 수 없기 때문에 특정 인덱스의 문자가 사용중인지 여부를 판단하는 배열이 필요하다.

 

 

출력되는 문자열의 문자 사이사이에 공백이 당연히 포함된다고 생각해서 공백까지 출력해서 맞왜틀을 반복했다.

 

출력 형식을 잘 확인하도록 하자.

 

#include<iostream>
#include<algorithm>
using namespace std;
int L, C, st;
char n[15], arr[15];
bool isused[15];
bool Check() {
	int cntM = 0, cntJ = 0;
	for (int i = 0; i < L; i++) {
		if (arr[i] == 'a' or arr[i] == 'e' or
			arr[i] == 'i' or arr[i] == 'o' or arr[i] == 'u')
			cntM++;
		else
			cntJ++;
	}
	if (cntM >= 1 && cntJ >= 2) //모음1, 자음2개가 있어야 true
		return true;
	else
		return false;
}
void func(int k) {
	if (k == L) {
		if (Check()) {
			for (int i = 0; i < L; i++)
				cout << arr[i];
			cout << '\n';
		}
		return;
	}
	if (k == 0) st = 0;
	for (int i = st; i < C; i++) {
		if (!isused[i]) {
			arr[k] = n[i];
			isused[i] = true;
			st = i;
			func(k + 1);
			isused[i] = false;
		}
	}
}
int main(void) {
	cin >> L >> C;
	for (int i = 0; i < C; i++)
		cin >> n[i];
	sort(n, n + C);
	
	func(0);
	return 0;
}

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

 

 

15666번: N과 M (12)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해야 한다.

www.acmicpc.net

사용했던 숫자를 여러 번 사용할 수 있다.

 

따라서 숫자 사용 여부를 관리하는 배열이 필요하지 않다.

 

수열을 벡터에 쌓고, 원하는 길이만큼 쌓이면 set에 넣고 벡터를 비워주는 것을 반복한다.

 

set에는 중복되는 원소가 들어갈 수 없다는 것을 활용했다.

 

 

 

#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
int m, n, num[8], arr[8], Start;
set<vector<int> > st;
void func(int k) {
	if (k == m) {
		vector<int> v;
		for (int i = 0; i < m; i++)
			v.push_back(arr[i]);
		st.insert(v);
		v.clear();
		return;
	}
	if (k == 0) Start = 0;
	for (int i = Start; i < n; i++) {
		arr[k] = num[i];
		Start = i;
		func(k + 1);
	}

}
int main(void) {
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		cin >> num[i];
	sort(num, num + n);
	func(0);
	set<vector<int> >::iterator itr;
	for (itr = st.begin(); itr != st.end(); itr++) {
		vector<int> tmp = *itr;
		for (int i = 0; i < tmp.size(); i++)
			cout << tmp[i] << ' ';
		cout << '\n';
	}
	return 0;
}

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

 

15665번: N과 M (11)

한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다. 수열은 사전 순으로 증가하는 순서로 출력해야 한다.

www.acmicpc.net

 

숫자의 입력이 중복되어서 들어올 수 있다.

 

결과로 나오는 수열은 오름차순으로 정렬되는 상태여야 한다. 하지만 입력은 정렬되어서 들어오는 것이 아니기 때문에 입력을 모두 받은 이후에 오름차순 정렬을 해줘야 한다.

 

또한, 사용했던 숫자를 재사용할 수 있기 때문에 숫자의 사용 중 여부를 판단하는 배열 또한 필요하지 않다.

 

그리고 수열이 중복되어서 생성될 수 있는데, 이는 중복을 허용하지 않는 set을 활용해서 처리했다.

 

#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
int m, n, arr[7], num[7];
set<vector<int > > st;
vector<int> v;
void func(int k) {
	if (k == m) {
		for (int i = 0; i < m; i++) {
			v.push_back(arr[i]);
		}
		st.insert(v);
		v.clear();
		return;
	}
	for (int i = 0; i < n; i++) {
		arr[k] = num[i];
		func(k + 1);
	}
}
int main() {
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		cin >> num[i];
	sort(num, num + 1);
	func(0); //0번까지 채워져있다
	set<vector<int> >::iterator itr;
	for (itr = st.begin(); itr != st.end(); itr++) {
		vector<int> tmp = *itr;
		for (int i = 0; i < tmp.size(); i++)
			cout << tmp[i] << ' ';
		cout << '\n';
	}
	return 0;
}

'알고리즘 문제 풀이 > 백준 온라인 저지' 카테고리의 다른 글

백준 1799번: 비숍 (C++)  (0) 2019.07.24
백준 15666번: N과 M (12) (C++)  (0) 2019.07.23
백준 6603번: 로또 (C++)  (0) 2019.07.22
백준 5427번: 불 (C++)  (0) 2019.07.22
백준 2573번: 빙산 (C++)  (0) 2019.07.22

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

 

6603번: 로또

문제 독일 로또는 {1, 2, ..., 49}에서 수 6개를 고른다. 로또 번호를 선택하는데 사용되는 가장 유명한 전략은 49가지 수 중 k(k>6)개의 수를 골라 집합 S를 만든 다음 그 수만 가지고 번호를 선택하는 것이다. 예를 들어, k=8, S={1,2,3,5,8,13,21,34}인 경우 이 집합 S에서 수를 고를 수 있는 경우의 수는 총 28가지이다. ([1,2,3,5,8,13], [1,2,3,5,8,21], [1,2,3,5,8,34], [1,2

www.acmicpc.net

 

n개의 숫자중에 6개를 뽑아서 수열을 만들고, 오름차순 정렬하여 출력해주면 된다.

 

입력으로 들어오는 숫자는 정렬된다는 조건이 없다는 것을 주의해야 한다.

 

 

1. next_permutation 활용 구현

 

#include<iostream>
#include<algorithm>
using namespace std;
int n, a[50], Sel[50];
int main(void) {
	while (1) {
		cin >> n;
		if (n == 0) return 0;
		
		for (int i = 0; i < n; i++)
			cin >> a[i];
		sort(a, a + n);
		for (int i = 0; i < n; i++) {
			if (i < 6) Sel[i] = 0;
			else Sel[i] = 1;
		}
		
		do {
			for (int j = 0; j < n; j++) {
				if (!Sel[j]) cout << a[j] << ' ';
			}
			cout << '\n';
		} while (next_permutation(Sel, Sel + n));
		cout << '\n';
	}
	return 0;
}

 

 

2. 백트래킹 활용 구현

 

마찬가지로 사용할 수 있는 수들을 입력 받은 이후에, 수열을 오름차순으로 만들어내기 위해서 정렬을 수행한다.

 

조합이기 때문에 매 재귀마다 0부터 확인하는 것이 아니라 이전 재귀에서 사용했던 숫자의 위치를 파악해서 그 숫자 이후로 사용할 수 있는 숫자를 찾는다.

 

#include<iostream>
#include<algorithm>
using namespace std;
int n, num[50], arr[7];
bool isused[50];
int st;
void func(int k) {
	//여기서 수열의 길이 k
	if (k == 6) {
		for (int i = 0; i < k; i++) {
			cout << arr[i] << ' ';
		}
		cout << '\n';
		return;
	}
	
	if (k == 0) st = 0; //0일때 초기화
	for (int i = st; i < n; i++) { //조합으로 만들기 위해 st부터. 0부터 뽑으면 순열(순서가 다르면 다른 수열)
		if (!isused[i]) {
			isused[i] = true;
			arr[k] = num[i];
			st = i;
			func(k + 1);
			isused[i] = false;
		}
	}
}
int main(void) {
	while (1) {
		cin >> n;
		if (n == 0) break;
		for (int i = 0; i < n; i++) {
			cin >> num[i];
		}
		sort(num, num + n);
		func(0);
		cout << '\n';
	}
}

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

 

5427번: 불

문제 상근이는 빈 공간과 벽으로 이루어진 건물에 갇혀있다. 건물의 일부에는 불이 났고, 상근이는 출구를 향해 뛰고 있다. 매 초마다, 불은 동서남북 방향으로 인접한 빈 공간으로 퍼져나간다. 벽에는 불이 붙지 않는다. 상근이는 동서남북 인접한 칸으로 이동할 수 있으며, 1초가 걸린다. 상근이는 벽을 통과할 수 없고, 불이 옮겨진 칸 또는 이제 불이 붙으려는 칸으로 이동할 수 없다. 상근이가 있는 칸에 불이 옮겨옴과 동시에 다른 칸으로 이동할 수 있다. 빌딩

www.acmicpc.net

 

두 가지를 동시에 신경써야 했다. 불의 확산과 사람의 이동.

 

특히 신경써야 할 부분은

 

1. 이제 불이 붙으려는 칸으로 이동할 수 없다

2. 현재 위치로 불이 옮겨옴과 동시에 다른 칸으로 이동할 수 있다.

 

이 두가지이다.

 

 

기본적인 접근은 불을 먼저 확산시킬 것인가, 사람을 먼저 움직일 것인가를 결정하는 것이다.

 

1. 불의 확산 이후 사람의 이동

불을 먼저 확산시키면 사람의 위치가 지워질 가능성이 높다. 따라서 사람의 시작 위치를 미리 관리해주어야 한다.

 

이후에는 사람의 이동거리(매 초 사람의 위치)를 따로 둘 것이기 때문에 초반에 불의 이동에 의해서 @의 위치가 손실되는 것만 신경써주면 이후로는 신경쓸 부분이 없다. 즉 위에서 신경써야 할 부분이라고 언급한 2번 조건이 상관 없어지는 것이다.

 

당연하게도 불을 먼저 확산시켰으므로 불이 붙으려는 칸으로는 당연하게 이동할 수 없다. 구현을 그렇게 했으니까.

 

 

2. 사람의 이동 이후 불의 확산

이 방식은 먼저 사람의 이동을 고려한다. 당연하게도 이동하려는 곳은 비어있는(불도 벽도 아닌) 곳이어야 한다.

 

그리고 그 이동하려는 곳의 4방향을 미리 탐색해서 불이 있는지 없는지를 확인해야 한다. 이것으로 신경써야 할 부분 1을 피할 수 있다.

 

즉 사람의 이동을 구현하는 데에 두 단계가 필요하다는 것이다.

 

또한 사람의 이동을 먼저 구현했기 때문에, 이미 이동한 뒤에 불이 확산하는 것이므로 원래 사람이 있던 자리에 불이 번지는 것은 이제 상관이 없어진다. 이렇게 신경써야 할 부분 2를 해결할 수 있다.

 

 

두 가지 모두 생각해 본 결과, 1의 구현이 더 간단한 것 같았으므로 1번으로 구현 방향을 잡았다.

 

#include<iostream>
#include<queue>
using namespace std;
int Col, Row, dis[1001][1001]; //dis로 사람의 매초 위치(그 지점까지 최단거리)를 관리
char m[1001][1001]; //빌딩 상태
bool Fire[1001][1001]; //불의 존재 여부 관리
int dr[4] = { 0,0,1,-1 };
int dc[4] = { 1,-1,0,0 };
queue<pair<int, int> > fireQ; //불의 번짐을 관리할 큐
queue < pair<int, int> > q; //사람의 이동을 관리할 큐
void Fbfs() {
	while (!q.empty()) { //사람이 움직일 위치가 없으면 더 이상의 진행이 무의미

		int num = fireQ.size(); //매 초 있던 불꽃으로만 확산 진행
		while (num--) { 
			pair<int, int> cur = fireQ.front();
			fireQ.pop();
			for (int i = 0; i < 4; i++) {
				int nr = cur.first + dr[i];
				int nc = cur.second + dc[i];
				if (nr < 0 || nc < 0 || nr >= Row || nc >= Col) continue;
				if (m[nr][nc] == '#' || Fire[nr][nc]) continue; //벽 or 이미 불이 존재하는 곳이면 continue
				//printf("\n%d %d 추가\n", nr, nc);
				fireQ.push({ nr, nc });
				Fire[nr][nc] = true;
				m[nr][nc] = '*';
			}
		}

		int Run = q.size(); //불과 마찬가지로 매초 한칸씩 움직이게 통제
		while (Run--) {
			pair<int, int> cur = q.front();
			q.pop();
			for (int i = 0; i < 4; i++) {
				int nr = cur.first + dr[i];
				int nc = cur.second + dc[i];
				if (nr < 0 || nc < 0 || nr >= Row || nc >= Col) {
					//탈출처리 -> 건물 끝까지 가는데 n초 걸렸고, 이제 넘어가니까 +1
					cout << dis[cur.first][cur.second] + 1 << '\n';
					return;
				}
				if (dis[nr][nc] >= 0 || m[nr][nc] != '.')continue; //이미 지나온 길이거나 이동할 수 있는 길이 아니면 continue
				q.push({ nr, nc });
				dis[nr][nc] = dis[cur.first][cur.second] + 1;
			}
		}
	}
	cout << "IMPOSSIBLE" << '\n';
}
void init() {
	for (int i = 0; i < Row; i++) {
		for (int j = 0; j < Col; j++) {
			dis[i][j] = -1; // 0이상이면 사람이 거쳐간 상태(방문 완료)
			Fire[i][j] = false;
 		}
	}
	while (!q.empty()) q.pop(); //탈출 조건에서 큐가 비지 않았는데 함수가 종료됨
	while (!fireQ.empty()) fireQ.pop();
}
int main() {
	int T;
	cin >> T;
	while (T--) {
		cin >> Col >> Row;
		
		init(); //TC가 여러개, 큐를 완전히 비우지 않고 끝내는 경우도 있으므로 큐도 초기화
		for (int i = 0; i < Row; i++) {
			for (int j = 0; j < Col; j++) {
				cin >> m[i][j];
				if (m[i][j] == '@') { //사람의 시작 위치를 담아둬야함. 불이 덮어 쓰기 때문에
					q.push({ i, j }); 
					dis[i][j]++;
				}
			}
		}
		

		for (int i = 0; i < Row; i++) {
			for (int j = 0; j < Col; j++) {
				if (m[i][j] == '*' && !Fire[i][j]) {
					fireQ.push({ i, j });
					Fire[i][j] = true;
				}
			}
		}
		Fbfs();
	}
	return 0;
}

 

 

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

 

2573번: 빙산

첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을 나타내는 M개의 정수가 한 개의 빈 칸을 사이에 두고 주어진다. 각 칸에 들어가는 값은 0 이상 10 이하이다. 배열에서 빙산이 차지하는 칸의 개수, 즉, 1 이상의 정수가 들어가는 칸의 개수는 10,000 개 이하이다. 배열의 첫 번째 행과 열, 마지

www.acmicpc.net

 

floodfill을 통해서 빙산이 두 개가 될 때까지 빙산이 녹는 과정을 구현하면 된다. 

 

빙산이 녹는 것을 구현할 때 주의할 사항은, 녹아서 0이 된 빙산을 같은 해야 원래 녹아있던 것으로 보지 않는 것이다.

 

이는 방문 처리 배열을 통해서 처리해주면 된다. 녹아서 바다가 되버린 빙산이라도, 방문처리가 되어있기 때문에, 방문 처리가 되지 않은 바다만 빙산을 녹이는 데에 영향을 끼칠 수 있게 구현을 하면 된다.

 

또 유의할 것은 방문처리 배열의 초기화이다. 빙산을 녹이는 과정과, 빙산의 개수를 파악하기 위해서 floodfill을 하는 과정은 모두 방문처리 배열을 이용한다. 이때 한 번 방문처리 배열을 사용했으면, 바로 초기화를 해줘야 한다.

 

#include<iostream>
using namespace std;
int R, C, m[300][300];
bool vis[300][300];
int dr[4] = { 0,0,1,-1 };
int dc[4] = { 1,-1,0,0 };
void dfs(int row, int col) {
	vis[row][col] = true;
	
	for (int i = 0; i < 4; i++) {
		int nr = row + dr[i];
		int nc = col + dc[i];
		
		if (nr < 0 || nc < 0 || nr >= R || nc >= C) continue;
		if (vis[nr][nc] || m[nr][nc] == 0) continue;
		dfs(nr, nc);
	}
}
int Flood() {
	int res = 0;
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			if (m[i][j] != 0 && !vis[i][j]) {
				res++;
				if (res > 1) break;
				dfs(i, j);
			}
		}
	}
	return res;
}
void init() {
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			vis[i][j] = false;
		}
	}
}
void Melt(int row, int col) {
	vis[row][col] = true;
	int cnt = 0;
	for (int i = 0; i < 4; i++) {
		int nr = row + dr[i];
		int nc = col + dc[i];

		if (nr < 0 || nc < 0 || nr >= R || nc >= C || vis[nr][nc]) continue;
		if (m[nr][nc] == 0) cnt++;
		else if (m[nr][nc] == 1) Melt(nr, nc);
	}
	if (m[row][col] - cnt <= 0) m[row][col] = 0;
	else
		m[row][col] = m[row][col] - cnt;
}

int main() {
	cin >> R >> C;
	for (int i = 0; i < R; i++) {
		for (int j = 0; j < C; j++) {
			cin >> m[i][j];
		}
	}

	int year = 0;
	while (1) {
		for (int i = 0; i < R; i++) {
			for (int j = 0; j < C; j++) {
				if (m[i][j] != 0 && !vis[i][j]) {
					Melt(i, j);
				}
			}
		}
		year++;
		init();
		if (Flood() > 1) {
			cout << year << '\n';
			break;
		}
		init();
		if (Flood() == 0) {
			cout << 0 << '\n';
			break;
		}
		init();
	}
	return 0;
}

 

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

 

1182번: 부분수열의 합

첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 20, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에 두고 주어진다. 주어지는 정수의 절댓값은 100,000을 넘지 않는다.

www.acmicpc.net

 

정수들의 개수와 목표로 하는 합을 입력받는다.

 

정수들로 만들 수 있는 길이가 1 이상인 부분 수열들을 구한다. 그리고 그 부분수열들의 합이 처음에 입력받은 S와 같은 부분수열의 개수를 구하면 되는 문제이다.

 

우선 N개의 숫자를 입력받았다고 가정하면, 1개, 2개, 3개 ... N개로 만들 수 있는 부분수열을 모두 구해야 한다.

 

그리고 매 부분수열마다 합을 계산해서 그 합이 s인지 아닌지 판단해주면 되는 문제이다.

 

즉 조합으로 바꿔서 생각할 수 있고, n이 20 이하로 작으니 백트래킹을 해도 무방하다.

 

직접 재귀로 구현할 수도 있지만 next_permutation을 이용해서 구현했다.

 

 

Sel 배열을 우선 만들어 둔다. 가령 6개 중에 4개를 조합으로 뽑는다면,

 

Sel 배열은 0, 0, 0, 0, 1 1 이렇게 두고 if(!Sel[i]) 일 때 수열이 만들어지게 된다.

 

이를 이용해서 1개부터 n개까지 부분수열을 구해주면 되겠다.

 

 

#include<iostream>
#include<algorithm>
using namespace std;
int n, s, a[20], Sum = 0, Sel[20], cnt = 0;
int main(void) {
	cin >> n >> s;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	for (int i = 0; i < 20; i++)
		Sel[i] = 1;
	for (int i = 0; i < n; i++) {
		Sel[i] = 0; //하나씩 0으로 바꿈
		do {
			Sum = 0;
			for (int j = 0; j < n; j++) {
				if (!Sel[j]) Sum += a[j];
				//if (!Sel[j]) cout << a[j] << ' ';
			}
			if (Sum == s) cnt++;
		} while (next_permutation(Sel, Sel + n));
	}
	cout << cnt << '\n';
	return 0;
}

+ Recent posts