반응형

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

 

15686번: 치킨 배달

크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸

www.acmicpc.net

 

 

💡 치킨 배달

 

 

✅ 문제 풀이
  • 이 문제는 완전탐색을 이용해 조합을 만들어내는 것이 관건이다.
  • STL을 사용할 수 도 있지만, 직접 조합을 구해내는 알고리즘이 있다.
  • 완전탐색을 이용하면 조합을 구할 수 있다. 
  • 만약 치킨 집 총 5개 중에서 3곳만 살려두려고 할 때, 그 세곳으로 정할 수 있는 경우의 수를 구해보자.
  • 각 치킨 집을 1-5로 번호를 부여하면, {1,2,3},{1,2,4},{1,2,5}....{3,4,5}로 구할 수 있다.
  • 이때 주의할 점은 조합의 경우 순열과 달리 순서를 고려하지 않기때문에 {1,2,3}={2,1,3}이다. 따라서 시작이 2였으면, 1은 고려하지 않기로 한다. 1의 경우는 1이 시작이었을 때 이미 다 선택되었기 때문이다.
  • 조합을 구하는 알고리즘에 대한 자세한 설명은 페이지를 참고한다.

 

  • 따라서 나는 다음 순서로 이 문제를 해결하였다.
  • 치킨집의 위치를 저장하는 벡터 (chichen)을 생성하여 각 치킨 집의 위치를 저장해주었다. 그리고 벡터의 인덱스를 치킨집 번호로 고려하였다.
  • 각 집의 위치를 저장하는 벡터 (home)을 생성하여 각 집의 위치를 저장해주었다. 그리고 벡터의 인덱스를 집의 번호로 고려하였다.
  • 치킨집의 조합을 저장하는 벡터 (comb)를 생성하여 조합 결과를 저장해주었다. 이때 치킨집의 인덱스를 번호로 하였다.
  • 조합을 구할 때 각 치킨 집의 선택 여부를 고려하기 위한 벡터(visited)를 선언하였다.
  • 배열을 입력 받을 때, 값이 1이면 집의 위치를 저장하고, 값이 2이면 치킨집의 위치를 저장하도록 하였다.
  • 그리고 입력받은 m개의 치킨집으로만 이루어진 조합을 구하기 위해 DFS함수를 선언하고 호출하였다.
  • DFS함수는 조합을 생성한다.
  • 각 조합에서 시작 인덱스인 s와 현재 몇개까지 담았는지를 나타내는 cnt를 인자로 갖는다.
  • 함수 내에서는 cnt가 m이면, visited의 크기만큼 순회해서 값이 true인 인덱스 = 선택받은 치킨집 을 c라는 임시 vector에 저장하고, m개의 선택된 조합이 담긴 c를 comb에 저장하여 하나의 조합에 대해 저장하도록 했다. 그리고 이 경우에는 수행 후 바로 return하여 함수의 재귀호출을 멈춘다.
  • 위에 해당하지 않는다면 인자로 주어진 s 인덱스부터 시작하여 나머지 치킨집을 탐색한다. 이때 현재 치킨집의 visited가 true이면 건너뛰고, 그렇지 않으면 true로 해준뒤, DFS를 재귀호출하고, s는 동일하게, 하지만 방금 현재 인덱스를 선택했으므로 cnt+1을 하여 인자로 넘겨준다. 이를 호출하고 난 뒤에는, 또 선택되어져야 하기 때문에, false로 바꾸어준다.
  • 이런식으로 재귀호출을 하다가 cnt가 m이 되는 순간에 조합의 결과를 저장하는 것이다.
  • DFS함수를 호출할 때 시작 인덱스는 0, 그리고 현재까지 선택된 치킨집 개수도 0이므로, DFS(0,0)을 호출한다.
  • 그런다음, comb에 조합이 다 만들어졌기 때문에, 이제 계산만 하면 된다.
  • 모든 집은 각 조합 내에 있는 치킨 집과의 거리 중 최소 거리를 구한다.
  • 해당 조합에서 모든 집과 치킨 집과의 누적 거리를 구한다.
  • 각 조합 간의 누적 거리들 중 최소 값을 구한다.
  • 그 값이 최소 거리가 된다.

 

✏ 코드 전문
#include<iostream>
#include<vector>
#include<algorithm>
#include<limits.h>//c++17에서는 INT_MAX 정의를 여기서 가져와야해서 필요

using namespace std;
//1. 치킨집의 위치를 갖는 벡터(chicken)를 선언하고 저장해준다. 치킨집의 번호는 인덱스로 구분한다
//2. 일반집의 위치를 갖는 벡터(home)를 선언하고 저장해준다.
//3. 치킨집의 조합을 구한다. 완전탐색으로. 이때 치킨집 번호로 0~chichen.size-1. 조합 결과를 벡터(comb)에 저장한다.

int n, m;
vector<bool> visited;
vector<vector<int>> comb;

void DFS(int s, int cnt) {
	if (cnt == m) {
		vector<int> c;
		for (int i = 0; i < visited.size(); i++) {
			if (visited[i] == true) {
				c.push_back(i);
			}
		}
		comb.push_back(c);
		return;
	}

	for (int i = s; i < visited.size(); i++) {
		if (visited[i]) continue;
		visited[i] = true;
		DFS(i, cnt + 1);
		visited[i] = false;
	}
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> n >> m;

	int temp;
	vector<pair<int, int>> chichen;
	vector<pair<int, int>> home;

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> temp;
			if (temp == 1) {
				home.push_back(pair<int, int>(i, j));
			}
			else if (temp == 2) {
				chichen.push_back(pair<int, int>(i, j));
			}
		}
	}

	visited.resize(chichen.size());

	DFS(0, 0);

	int answer = INT_MAX;//조합에서 가장 최소로 나올 수 있는 값
	for (int i = 0; i < comb.size(); i++) {
		int dis = 0;//각 조합에서의 최소 거리
		for (int j = 0; j < home.size(); j++) {
			int m = INT_MAX;//내집-치킨집 최소 거리
			for (int k = 0; k < comb[i].size(); k++) {
				int index = comb[i][k];//치킨 집 번호
				m = min(m, abs(home[j].first - chichen[index].first) + abs(home[j].second - chichen[index].second));//내 집에서 이 치킨집까지 거리
			}
			dis += m;
		}
		answer = min(answer, dis);
	}

	cout << answer;
	return 0;

}
반응형
반응형

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

 

10026번: 적록색약

적록색약은 빨간색과 초록색의 차이를 거의 느끼지 못한다. 따라서, 적록색약인 사람이 보는 그림은 아닌 사람이 보는 그림과는 좀 다를 수 있다. 크기가 N×N인 그리드의 각 칸에 R(빨강), G(초록)

www.acmicpc.net

 

 

💡 적록색약

 

 

✅ 문제 풀이
  • 골드 치고 쉬운 dfs 문제
  • 보자마자 어떻게 풀어야 할지 접근방식이 바로 보이는 문제!!
  • 주의해야 할 점은 적록색약일 경우 R,G가 인접해있으면 같은 영역으로 본다는 점
  • 이런 영역 문제를 풀 때는 방문한 칸에 대해서는 방문 표시를 하기때문에, 두 가지 접근 방식에 대해서 서로 영향을 받지 않기 위해서 arr를 두개를 만들고 시작했다.
  • arr1은 적록색약이 아닌 사람이 보는 영역으로, R은 1, G는2, B는3 을 저장했다.
  • arr2는 적록색약인 사람이 보는 영역으로, R과 G는 1, B는 3을 저장했다. 이게 핵심이다. R과 G를 같은 값으로 배정하여 같은 영역으로 인식되도록 하였다.
  • 방문한 것에 대해서는 arr 값을 0으로 갱신하도록 하며, 방문 여부를 체크했다.
  • arr 크기만큼 순회하면서 각 칸이 0이 아니면 dfs 함수를 호출하여, 현재 위치와 현재 위치의 값, 그리고 참조할 arr를 인자로 넘겨주었다.
  • arr1과 arr2에 대해서 각각 취해야 하기 때문에 두번 씩 호출해 주었다.
  • dfs 함수 내 동작
  • 현재 칸을 방문했으므로 값을 0으로 갱신해준다.
  • 현재 칸을 기준으로 상하좌우가 arr의 범위내에 있고, 현재 칸과 값이 동일하면 같은 영역이므로 해당 칸에 대해서 DFS를 재귀호출하여 한 영역에 대해 계속 탐색하도록 하였다.
  • 다시 돌아와서, main의 2중 for문에서 dfs가 호출되었다는 것은 영역의 시작과 같은 의미이므로, dfs를 호출한 후에는 answer을 증가시켜 영역을 개수를 카운트 하였다.
  • 이때 적록색약이 아닌 경우는 answer1을, 적록색약인 경우는 answer2를 카운트 하도록 하였다.
  • 최종 answer들을 출력한다.

 

✏ 코드 전문
#include<iostream>
#include<vector>
#include<string>

using namespace std;

int n;
vector<vector<int>> arr1;
vector<vector<int>> arr2;

int answer1 = 0;
int answer2 = 0;

int dx[] = { -1,1,0,0 };
int dy[] = { 0,0,-1,1 };

void dfs(int i, int j, int num, vector<vector<int>> &arr) {
	arr[i][j] = 0;
	
	for (int k = 0; k < 4; k++) {
		int x = i + dx[k];
		int y = j + dy[k];
		if (x >= 0 && x < n&&y >= 0 && y < n&&arr[x][y] == num) {
			dfs(x, y, num, arr);
		}
	}
}

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

	cin >> n;

	arr1.resize(n, vector<int>(n));
	arr2.resize(n, vector<int>(n));

	string str;
	for (int i = 0; i < n; i++) {
		cin >> str;
		for (int j = 0; j < n; j++) {
			if (str[j] == 'R') {
				arr1[i][j] = 1;
				arr2[i][j] = 1;
			}
			else if (str[j] == 'G') {
				arr1[i][j] = 2;
				arr2[i][j] = 1;
			}
			else {
				arr1[i][j] = 3;
				arr2[i][j] = 3;
			}
		}
	}

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (arr1[i][j] != 0) {
				dfs(i, j, arr1[i][j], arr1);
				answer1++;
			}
			if (arr2[i][j] != 0) {
				dfs(i, j, arr2[i][j], arr2);
				answer2++;
			}
		}
	}
	
	cout << answer1 << " " << answer2;

	return 0;
}
반응형
반응형

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

 

2667번: 단지번호붙이기

<그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여

www.acmicpc.net

 

 

 

💡 단지번호붙이기

<그림 1>과 같이 정사각형 모양의 지도가 있다. 1은 집이 있는 곳을, 0은 집이 없는 곳을 나타낸다. 철수는 이 지도를 가지고 연결된 집의 모임인 단지를 정의하고, 단지에 번호를 붙이려 한다. 여기서 연결되었다는 것은 어떤 집이 좌우, 혹은 아래위로 다른 집이 있는 경우를 말한다. 대각선상에 집이 있는 경우는 연결된 것이 아니다. <그림 2>는 <그림 1>을 단지별로 번호를 붙인 것이다. 지도를 입력하여 단지수를 출력하고, 각 단지에 속하는 집의 수를 오름차순으로 정렬하여 출력하는 프로그램을 작성하시오.

[입력]
첫 번째 줄에는 지도의 크기 N(정사각형이므로 가로와 세로의 크기는 같으며 5≤N≤25)이 입력되고, 그 다음 N줄에는 각각 N개의 자료(0혹은 1)가 입력된다.

[출력]
첫 번째 줄에는 총 단지수를 출력하시오. 그리고 각 단지내 집의 수를 오름차순으로 정렬하여 한 줄에 하나씩 출력하시오.

 

 

 

✅ 문제 풀이
  • 주어진 배열에서 연속적인 1을 하나의 영역으로 보고, 그 영역의 크기와, 총 영역의 개수를 구하는 문제이다.
  • 2중 for문으로 순회하며 각 칸이 1인지 확인한다.
  • 1인 경우에는 count를 0으로 초기화하고, dfs를 호출한다.
  • dfs에는 현재 칸의 좌표값과, count, 그리고 2차원 배열의 정보를 인자로 넘겨준다.
  • dfs의 동작은 다음과 같다.
  • dfs가 호출되면, 현재 칸을 방문했으므로 배열의 값을 0으로 갱신해준다.
  • 그리고 해당 영역에 대한 크기를 구하기 위해서 count를 1 증가 시켜준다.
  • 현재 칸을 기준으로 동서남북 방향에 대해서 총 4번 탐색할 건데, 동서남북이 배열의 인덱스에 유효한지 확인하고, 그 배열 값이 1이라면 dfs를 재귀 호출하여 이어 탐색하도록 한다.
  • 이렇게 되면, 한 영역을 처음 방문하고 나서는 동일한 영역에 대해서는 재귀호출 시 다 처리 되기 때문에, main함수의 2중 for문 순회시에는 딱 각 영역의 시작만으로 구분할 수 있다. 따라서 main의 2중 for문 순회에서 배열 값이 1인 경우에는 dfs를 호출한 뒤, 영역의 개수를 하나 증가시키고, dfs 결과 계산된 해당 영역의 크기인 count를 영역의 크기만 저장하는 배열 cnt에 넣어둔다.
  • 2중 for문을 모두 순회하고 나면 sum에는 영역의 개수가 저장되는 것이고, cnt에는 각 영역의 크기가 저장되어 있을 것이다.
  • 문제에서 출력 시 영역의 크기는 오름차순으로 출력하도록 하라고 되어있으므로, sort함수를 사용하여 cnt 벡터를 오름차순 정렬 시켜주었다.
  • 그리고 sum과 cnt를 모두 출력해준다.

 

 

✏ 코드 전문
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>

using namespace std;

int n;

int dx[] = { -1,1,0,0 };
int dy[] = { 0,0,-1,1 };

void dfs(int x, int y, int &count, vector<vector<int>> &arr) {
	arr[x][y] = 0;
	count++;

	for (int i = 0; i < 4; i++) {
		int nx = x + dx[i];
		int ny = y + dy[i];
		if (nx >= 0 && nx < n&&ny >= 0 && ny < n&&arr[nx][ny] == 1) {
			dfs(nx, ny, count, arr);
		}
	}
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> n;

	vector<vector<int>> arr(n, vector<int>(n));

	for (int i = 0; i < n; i++) {
		string str;
		cin >> str;
		for (int j = 0; j < n; j++) {
			arr[i][j] = str[j]-'0';
		}
	}

	vector<int> cnt;
	int sum = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (arr[i][j] == 1) {
				int count = 0;
				dfs(i, j, count, arr);
				sum++;
				cnt.push_back(count);
			}
		}
	}

	sort(cnt.begin(), cnt.end());

	cout << sum<<"\n";
	for (int i = 0; i < cnt.size(); i++) {
		cout << cnt[i] << "\n";
	}

	return 0;
}
반응형
반응형

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

 

1260번: DFS와 BFS

첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사

www.acmicpc.net

 

 

💡 DFS와 BFS
그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방문할 수 있는 점이 없는 경우 종료한다. 정점 번호는 1번부터 N번까지이다.

[입력]
첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사이에 여러 개의 간선이 있을 수 있다. 입력으로 주어지는 간선은 양방향이다.

[출력]
첫째 줄에 DFS를 수행한 결과를, 그 다음 줄에는 BFS를 수행한 결과를 출력한다. V부터 방문된 점을 순서대로 출력하면 된다.

 

 

✅ 문제 풀이
  • DFS와 BFS의 로직을 나누어서 두번의 결과를 따로 구하고 출력하도록 하였다.
  • 일단 입력으로 받은 간선의 정보를 2차원 벡터에 저장하였다.
    이때 양방향 간선이라는 점을 고려하여, A->B로 B->A로의 간선 정보를 저장해준다.
  • S노드에서 시작하므로, S번의 visited를 true로 설정하고 시작한다.
  • 간선 정보를 담은 arr배열을, 각 노드에 대해서 오름차순 정렬해준다. 노드 번호가 낮은 것부터 방문해야 하기 때문이다.
  • dfs함수는, arr배열의 s번 vector를 탐색하는데, 먼저 방문하는 노드를 시작으로 또 dfs를 재귀호출하며 방문하도록 한다. 
  • bfs함수는, arr배열의 s번 vector를 탐색하는데, 각 노드를 탐색할 때마다 queue에 담아주고, 해당 벡터를 다 탐색하고 나서는 queue의 front부터 탐색하며 bfs를 재귀호출하도록 한다.
  • 각각의 결과는 dfs_result, bfs_result에 담은 후 출력한다.

 

 

✏ 코드 전문
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>

using namespace std;

void dfs(int s, vector<vector<int>> arr, vector<bool> &visited, vector<int> &dfs_result) {
	for (int i = 0; i < arr[s].size(); i++) {
		if (!visited[arr[s][i]]) {
			visited[arr[s][i]] = true;//방문
			dfs_result.push_back(arr[s][i]);
			dfs(arr[s][i], arr, visited, dfs_result);
		}
	}
}
void bfs(int S, vector<vector<int>> arr, vector<bool> &visited, vector<int> &bfs_result, queue<int> &q) {
	for (int i = 0; i < arr[S].size(); i++) {
		if (!visited[arr[S][i]]) {
			visited[arr[S][i]] = true;//방문
			q.push(arr[S][i]);
			bfs_result.push_back(arr[S][i]);
		}
	}
	while (!q.empty()) {
		int next = q.front();
		q.pop();
		bfs(next, arr, visited, bfs_result, q);
	}
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int N, M, S;
	cin >> N >> M >> S;

	vector<vector<int>> arr(N+1);
	vector<bool> visited1(N + 1, false);//dfs에서사용
	vector<bool> visited2(N + 1, false);//bfs에서사용
	queue<int> q;//bfs에서 사용
	vector<int> dfs_result;
	vector<int> bfs_result;
	for (int i = 0; i < M; i++) {//간선 정보 입력받기
		int n1, n2;
		cin >> n1 >> n2;

		arr[n1].push_back(n2);
		arr[n2].push_back(n1);
	}

	for (int i = 1; i < N + 1; i++) {//노드별로 간선 정보를 오름차순 정렬
		sort(arr[i].begin(), arr[i].end());
	}


	visited1[S] = true;
	visited2[S] = true;
	dfs_result.push_back(S);
	bfs_result.push_back(S);

	dfs(S, arr, visited1, dfs_result);
	bfs(S, arr, visited2, bfs_result, q);

	for (int i = 0; i < dfs_result.size(); i++) {
		cout << dfs_result[i]<<" ";
	}
	cout << "\n";
	for (int i = 0; i < bfs_result.size(); i++) {
		cout << bfs_result[i] << " ";
	}
	
	return 0;
}
반응형
반응형

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

 

1068번: 트리

첫째 줄에 트리의 노드의 개수 N이 주어진다. N은 50보다 작거나 같은 자연수이다. 둘째 줄에는 0번 노드부터 N-1번 노드까지, 각 노드의 부모가 주어진다. 만약 부모가 없다면 (루트) -1이 주어진다

www.acmicpc.net

 

💡 트리
트리에서 리프 노드란, 자식의 개수가 0인 노드를 말한다.
트리가 주어졌을 때, 노드 하나를 지울 것이다. 그 때, 남은 트리에서 리프 노드의 개수를 구하는 프로그램을 작성하시오. 노드를 지우면 그 노드와 노드의 모든 자손이 트리에서 제거된다.
예를 들어, 다음과 같은 트리가 있다고 하자.

현재 리프 노드의 개수는 3개이다. (초록색 색칠된 노드) 이때, 1번을 지우면, 다음과 같이 변한다. 검정색으로 색칠된 노드가 트리에서 제거된 노드이다.

이제 리프 노드의 개수는 1개이다.

[입력]
첫째 줄에 트리의 노드의 개수 N이 주어진다. N은 50보다 작거나 같은 자연수이다. 둘째 줄에는 0번 노드부터 N-1번 노드까지, 각 노드의 부모가 주어진다. 만약 부모가 없다면 (루트) -1이 주어진다. 셋째 줄에는 지울 노드의 번호가 주어진다.

[출력]
첫째 줄에 입력으로 주어진 트리에서 입력으로 주어진 노드를 지웠을 때, 리프 노드의 개수를 출력한다.

 

 

✅ 문제 풀이
  • 각 노드에 대한 자식 노드를 저장하기 위해서, vector 형태의 크기가 50인 배열 tree을 생성해주었다.
    vector<int> tree[50]
  • 입력으로 N이 1부터 50까지 들어올 수 있기 때문에, 노드는 0~49임을 고려한 것이다.
  • 차례대로 입력받을 때 0번 노드부터 부모를 입력받는다.
  • 따라서 -1인 경우를 제외하고, 나머지 경우에 대해서는 입력받은 값을 p라고 했을 때, tree[p]에다가 해당 노드를 저장해준다.
    vector형태니까, push_back(해당 노드) 해주면 된다.
  • 그럼 모든 노드에 대해서 바로 아래 자식 노드 리스트를 가지게 된다. 당연히, 자식이 없는 노드의 벡터는 비어있을 것이다.
  • 그리고 삭제된 노드인지, 리프노드인지를 구별하기 위해 bool타입의 배열 leaf를 생성해서 false로 초기화 해주었다.
  • 삭제할 노드를 입력받고, 해당 노드를 기준으로 dfs함수를 호출한다.
  • dfs 함수에서는 다음과 같이 동작한다.
    입력 : tree, 삭제할 노드 node, leaf
    로직
    - 삭제할 노드의 자식 노드들을 순회한다. 즉 tree[node]를 순회한다.
    - 이 때 자식 노드를 num에 저장해두고, 자식의 자식들을 순회하기 위해 dfs를 호출하여 삭제할 노드로 num을 넘겨준다.
    -그리고 해당 노드에 대해 모두 순회했으면, tree[node]를 clear하여 모두 삭제해주고, 이는 삭제된 노드이므로 leaf[node]를 true로 변경해준다.
  • 입력한 삭제 노드에 대해 위 로직을 따르고 나면, 이제 입력 값만을 부모에서 삭제해주면 된다.
  • 부모를 찾기위해서 2중 for문을 통해 입력받은 값을 자식으로 갖는 노드가 있는지 순회하고, 일치하다면 해당 부모 노드의 배열에서 삭제하도록 한다. 그리고 순회를 즉시 멈춘다.
  • 이제 삭제할 노드를 기점으로 모두 삭제되었으므로, tree배열을 순회하면서 leaf가 false이고(=삭제되지 않은노드), tree[i]의 size가 0인 노드를 카운트 하여 출력한다.  

 

☑ 코드 전문
#include<iostream>
#include<vector>
using namespace std;

void dfs(vector<int> tree[], int node, bool leaf[]) {
	for (int i = 0; i < tree[node].size(); i++) {
		int num = tree[node][i];
		dfs(tree, num, leaf);
	}
	tree[node].clear();
	leaf[node] = true;
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int N;
	cin >> N;

	vector<int> tree[50];
	bool leaf[50] = {false};//false->true 개수만 세기
	for (int i = 0; i < N; i++) {
		int p;
		cin >> p;
		if (p == -1) continue;
		else {
			tree[p].push_back(i);
		}
	}

	int node;
	cin >> node;

	dfs(tree, node, leaf);

	bool br=false;
	for (int i = 0; i < N; i++) {
		for (auto it = tree[i].begin(); it != tree[i].end(); it++) {
			if (*it==node) {
				tree[i].erase(it);
				br = true;
				break;
			}
		}
		if (br) break;
	}

	int answer = 0;

	for (int i = 0; i < N; i++) {
		if (!leaf[i]&&tree[i].size() == 0) answer++;
	}

	cout << answer;
    
    return 0;
}

 

반응형
반응형

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

 

1012번: 유기농 배추

차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 

www.acmicpc.net

 

💡 유기농 배추

차세대 영농인 한나는 강원도 고랭지에서 유기농 배추를 재배하기로 하였다. 농약을 쓰지 않고 배추를 재배하려면 배추를 해충으로부터 보호하는 것이 중요하기 때문에, 한나는 해충 방지에 효과적인 배추흰지렁이를 구입하기로 결심한다. 이 지렁이는 배추근처에 서식하며 해충을 잡아 먹음으로써 배추를 보호한다. 특히, 어떤 배추에 배추흰지렁이가 한 마리라도 살고 있으면 이 지렁이는 인접한 다른 배추로 이동할 수 있어, 그 배추들 역시 해충으로부터 보호받을 수 있다. 한 배추의 상하좌우 네 방향에 다른 배추가 위치한 경우에 서로 인접해있는 것이다.
한나가 배추를 재배하는 땅은 고르지 못해서 배추를 군데군데 심어 놓았다. 배추들이 모여있는 곳에는 배추흰지렁이가 한 마리만 있으면 되므로 서로 인접해있는 배추들이 몇 군데에 퍼져있는지 조사하면 총 몇 마리의 지렁이가 필요한지 알 수 있다. 예를 들어 배추밭이 아래와 같이 구성되어 있으면 최소 5마리의 배추흰지렁이가 필요하다. 0은 배추가 심어져 있지 않은 땅이고, 1은 배추가 심어져 있는 땅을 나타낸다.
1100000000
0100000000
0000100000
0000100000
0011000111
0000100111

[입력]
입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트 케이스에 대해 첫째 줄에는 배추를 심은 배추밭의 가로길이 M(1 ≤ M ≤ 50)과 세로길이 N(1 ≤ N ≤ 50), 그리고 배추가 심어져 있는 위치의 개수 K(1 ≤ K ≤ 2500)이 주어진다. 그 다음 K줄에는 배추의 위치 X(0 ≤ X ≤ M-1), Y(0 ≤ Y ≤ N-1)가 주어진다. 두 배추의 위치가 같은 경우는 없다.

[출력]
필요한 배추흰지렁이의 개수를 구하라.

 

 

✅ 문제 풀이

 

값이 1이면서 동서남북으로 인접한 1이 존재할 때 한 영역이 되기 때문에, 한 영역안에서는 모든 1이 서로 인접한 1이 존재한다는 것이다. 따라서 어떤 값이 1이면 연쇄적으로 인접한 값을 마주할 때 까지 탐색하여 하나의 영역으로 구할 수 있다. 따라서 이 문제는 DFS로 해결할 수 있다.

 

  1. 먼저 매 테스트 케이스마다 배열에 값을 저장한다. 입력으로 받는 k개의 배추 위치에 대해 1로 저장하고 나머지는 0으로 저장하면 된다.
  2. 배열의 크기만큼 순회하는데, 값이 1이면 첫번 째 영역에 대한 시작이므로, count를 1 증가시킨 후, dfs 함수를 호출한다.
  3. dfs 함수에서는 현재 값이 1인 위치와, arr를 &로 넘겨준다. &로 넘겨주는 이유는 방문한 칸에 대해서는 arr의 값을 수정해주기 위해서이다.
  4. dfs 함수가 호출되면, 현재 인자로 받은 위치의 값을 0으로 바꾸어준다. 그 이유는 방문했음을 나타내기 위함과, 나중에 main의 for문에서 재방문 하지 않게 하기 위함이다.=> 이 문제의 핵심이다.
  5. 그리고 현재의 위치를 기준으로 동서남북 방향에 있는 값이 1인지 확인한다.
  6. 1이라면 같은 영역이므로, 그 칸을 기준으로 dfs 함수를 호출하도록 한다.
  7. 따라서 위의 과정을 반복하다 보면 동일한 영역에 해당하는 값들이 1에서 모두 0으로 바뀌게 되고, main문에서 해당 영역에 해대 시작했던 탐색이 끝나게 되어 하나의 영역을 탐색할 수 있게 되는 것이다.
  8. 위 과정을 거친 후 최종 count 값이 바로 영역의 개수가 되고, 필요한 지렁이의 개수가 된다.

 

☑ 코드 전문
#include<iostream>
#include<vector>

using namespace std;

int dx[] = { -1,1,0,0 };
int dy[] = { 0,0,-1,1 };

void dfs(int x, int y, vector<vector<int>> &arr) {
	arr[x][y] = 0;

	for (int n = 0; n < 4; n++) {
		int r = x + dx[n];
		int c = y + dy[n];
		if (r>=0&&r<arr.size()&&c>=0&&c<arr[0].size()&&arr[r][c] == 1) {
			dfs(r, c, arr);
		}
	}
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	int T;//테스트 케이스 개수
	cin >> T;

	for (int i = 0; i < T; i++) {
		int M, N, K;//가로, 세로, 총 배추 개수
		cin >> M >> N >> K;
		vector<vector<int>> arr(N, vector<int>(M, 0));//배추밭

		for (int j = 0; j < K; j++) {
			int x, y;
			cin >> y >> x;
			arr[x][y] = 1;
		}//배추 심기

		int count = 0;//배추흰지렁이의 개수

		for (int j = 0; j < N; j++) {
			for (int k = 0; k < M; k++) {
				if (arr[j][k] == 1) {
					count++;
					dfs(j, k, arr);
				}
			}
		}

		cout << count << "\n";

	}
}

 

 

반응형

+ Recent posts