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

 

16234번: 인구 이동

N×N크기의 땅이 있고, 땅은 1×1개의 칸으로 나누어져 있다. 각각의 땅에는 나라가 하나씩 존재하며, r행 c열에 있는 나라에는 A[r][c]명이 살고 있다. 인접한 나라 사이에는 국경선이 존재한다. 모

www.acmicpc.net

 

풀이 과정

 조건에 맞는 그래프들을 연결하고 연결된 그래프들의 값을 갱신하며 시간을 카운트하는 문제이다. 먼저 조건에 맞는 그래프를 link_map()함수로 연결시켰다. 그리고 모든 노드를 방문하며 dfs()가 실행되었을 경우, dfs()에서 연결 컴포넌트와 요소의 합을 구하고 fill_dfs()를 호출해서 해당 연결 컴포넌트의 값들을 채웠다. 그리고 day_cnt를 더해주며 실행된 날짜를 세며 정답을 구했다.

 

느낀 점

 순탄했던 것은 아니지만 생각보다 순탄했다. 우선 내 설계대로 풀어, 결과적으로 정답을 도출해냈다. 하지만 실수가 있어 디버깅을 하며 찾아냈다. 우선 run()의 while()문이 1회 완료되면 테스트 출력을 해보며 오류를 찾으려 했다. 우선 실행했을 때 정상적으로 바뀌는 경우도 있었지만 일부 경우 4방향 탐색을 수행할 때, 위, 오른쪽을 탐색하고 대각선으로 탐색을 하는 경우가 있었다. 4방향 탐색을 해주는 함수들을 살펴봤지만 이상이 없었고, 4방향 탐색을 위해 만든 my, mx에서 오탈자를 발견했다. ','가 와야할 곳에 '.'이 오게 되어서 오류가 발생했다는 걸 확인했다. 처음에 두어번은 의심하며 봤지만 워낙 비슷하게 생긴 문자들이라 그냥 지나치고 다른 곳에서 시간을 쏟고 말았다. 의심이 간다면 처음 검사할 때 꼼꼼히 하는 습관을 들이자.

 

#include<bits/stdc++.h>
using namespace std;

const int NMX = 51;
int visited[NMX][NMX], mp[NMX][NMX];
int my[4] = { 1, 0, -1, 0 }, mx[4] = { 0, 1, 0, -1 };
int n, bot, top, cnt, sum, day_cnt;
vector<pair<int, int>> adj[NMX][NMX];

// 연결 컴포넌트 개수, 합 연산
bool dfs(int y, int x) {
	if (visited[y][x]) return false; //방문하지 않은 그래프만 방문
	visited[y][x] = 1; // 첫 방문 처리
	sum += mp[y][x]; //연결 컴포넌트 요소 합 구하기
	for (int i = 0; i < adj[y][x].size(); i++) {
		dfs(adj[y][x][i].first, adj[y][x][i].second); //연결되 그래프로 들어감
	}
	cnt++; //연결된 그래프 개수를 위한 cnt
	return true;
}

//연결 컴포넌트를 floor(합/개수) 으로 채움
void fill_dfs(int y, int x, int sm) {
	if (visited[y][x] != 1) return; // visited == 1인 곳만 처리
	visited[y][x] += 1; //재방문 처리
	mp[y][x] = sm; //값 대입
	for (int i = 0; i < adj[y][x].size(); i++) {
		fill_dfs(adj[y][x][i].first, adj[y][x][i].second, sm); //연결된 그래프로 들어감
	}
	return;
}

//visited와 그래프 연결 리스트 초기화
void reset() {
	fill(&visited[0][0], &visited[0][0] + NMX * NMX, 0);
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			adj[i][j].clear();
		}
	}
	return;
}

//top, bot 값을 기준으로 그래프 연결 리스트 작성
bool link_map() {
	int flag = false;
	for (int i = 0; i < n; i++) { //모든 노드를 방문하며
		for (int j = 0; j < n; j ++) {
			for (int k = 0; k < 4; k++) { //노드 4방향 탐색
				int ny = my[k] + i;
				int nx = mx[k] + j;
				if (ny < 0 || ny >= n || nx < 0 || nx >= n) continue; //조건에 만족해야 실행
				int gap = abs(mp[i][j] - mp[ny][nx]); 
				if (gap <= top && gap >= bot) { //두 노드가 조건에 만족하면 서로 연결
					adj[i][j].push_back({ ny, nx });
					adj[ny][nx].push_back({ i, j });
					flag = true; //연결된 그래프가 남아 있음을 알림
				}
			}
		}
	}
	return flag;
}

//입력
void input() {
	cin >> n >> bot >> top;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> mp[i][j];
		}
	}
	return;
}

//출력
void output() {
	cout << day_cnt;
	return;
}

//실행
void run() {
	while (link_map()) { // 새롭게 연결된 그래프가 있다면 실행
		for (int i = 0; i < n; i++) {//모든 그래프 방문
			for (int j = 0; j < n; j++) {
				cnt = 0; sum = 0; //그래프 개수와 그래프 요소 합 초기화
				if (dfs(i, j)) fill_dfs(i, j, sum / cnt); //dfs()에서 방문 실행되었을때, 요소들을 새롭게 채움
			}
		}
		day_cnt++; //실행된 날짜를 카운트
		reset(); //visited와 연결 리스트를 초기화
	};
	return;
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	//입력
	input();
	//실행
	run();
	//출력
	output();
	return 0;
}

'알고리즘 : C++ > BaekJoon' 카테고리의 다른 글

백준 2852번 - NBA 농구  (0) 2023.05.28
백준 10709번 - 기상캐스터  (0) 2023.05.28
백준 2589번 - 보물섬  (0) 2023.05.25
백준 2870번 - 수학 숙제  (0) 2023.05.23
백준 15686번 - 치킨 배달  (0) 2023.05.23

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

 

2589번: 보물섬

보물섬 지도를 발견한 후크 선장은 보물을 찾아나섰다. 보물섬 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 각 칸은 육지(L)나 바다(W)로 표시되어 있다. 이 지도에서

www.acmicpc.net

 

풀이 과정

 연결 컴포넌트 중 가장 큰 최단거리를 구하는 문제이다. BFS()로 노드를 탐색하며 최단 거리를 구하고 MAX값을 갱신해주면 된다. 단, 최단거리는 같은 연결 컴포넌트더라도, 거리 측정을 시작하는 노드 위치에 따라 바뀐다. 따라서,  매번 visited를 초기화 해주고 'L' 마다 BFS()를 실행하며 최단 거리를 구하고 MAX를 갱신했다.

 

느낀 점

 'L'일 경우에만 BFS()가 실행 되도록 했어야 했는데, 일단 BFS를 실행하고 첫 노드를 큐에 넣은 후부터 검사를 했다. 이때문에 오답을 한 번 받게 되었다. 항상 엄밀한 조건 설정이 필요하다.

 

#include<bits/stdc++.h>
using namespace std;

const int NMX = 51;
int n, m, ret;
int visited[NMX][NMX], my[4] = {1, 0, -1, 0}, mx[4] = {0, 1, 0, -1};
char temp;
bool mp[NMX][NMX];
//BFS() 구현 : 최단 거리 반환
int bfs(int y, int x) {
	if (!mp[y][x]) return 0;
	int mx_cnt = 0;
	queue < pair<int, int>> q;
	q.push({ y, x });
	visited[y][x] = 1;

	do {
		for (int i = 0; i < 4; i++) {
			int ny = my[i] + q.front().first;
			int nx = mx[i] + q.front().second;
			if (ny < 0 || ny >= n || nx < 0 || nx >= m || visited[ny][nx] != 0 || !mp[ny][nx]) continue;
			visited[ny][nx] = visited[q.front().first][q.front().second] + 1;
			mx_cnt = max(mx_cnt, visited[ny][nx]);
			q.push({ ny, nx });
		}
		q.pop();
	} while (q.size());

	return mx_cnt;
}
//'L'과 'W'에 따라 지도 입력
void input() {
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> temp;
			if (temp == 'W') mp[i][j] = 0;
			else mp[i][j] = 1;
		}
	}
	return;
}
//BFS()를 실행하며 보물 간 최대 거리 구하기
void find_ret() {
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			fill(&visited[0][0], &visited[0][0] + NMX * NMX, 0);
			ret = max(bfs(i, j), ret);
		}
	}
}
//결과 출력
void output() {
	cout << ret - 1 << "\n";
	return;
}

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

	input();

	find_ret();

	output();

	return 0;
}

'알고리즘 : C++ > BaekJoon' 카테고리의 다른 글

백준 10709번 - 기상캐스터  (0) 2023.05.28
백준 16234번 - 인구 이동  (0) 2023.05.25
백준 2870번 - 수학 숙제  (0) 2023.05.23
백준 15686번 - 치킨 배달  (0) 2023.05.23
백준 4659번 - 비밀번호 발음하기  (0) 2023.05.23

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

 

2870번: 수학숙제

종이에서 찾은 숫자의 개수를 M이라고 하면, 출력은 M줄로 이루어져야 한다. 각 줄에는 종이에서 찾은 숫자를 하나씩 출력해야 한다. 이때, 비내림차순으로 출력해야 한다. 비내림차순은 내림차

www.acmicpc.net

 

풀이 과정

 문자열을 받고 숫자를 골라 오름차순으로 정렬 후 출력하는 문제이다. 단, 입력 문자열의 최대 길이가 100이므로 정수형으로 변환해서 하는 것이 아닌, 문자열 자체를 정수형으로 다듬고 대소를 비교해서 출력 해야한다. 문자열을 입력받고 _prev를 통해서 이전 값이 숫자이고 다음 값이 문자라면 token을 rez()에서 앞에 붙은 0을 제거해주고 ret에 넣었다. 만약 그게 아니고 s[i]가 숫자라면 token에 계속해서 더해줬다. 한 줄의 문자열 검사가 끝났을 때, 토큰에 남은 숫자가 있다면 rez() 처리를 해준 후, ret에 넣었다. 그리고 문자열 sort()를 위해서 cmp() 함수를 작성해줬고, 자릿수가 같을 경우 첫 자리부터 대소를 비교하고 이외의 경우 자릿수로 대소를 비교했다. 

 

느낀 점

 단순히 보고 접근했지만 생각보다 까다로웠다. 두 번째 푸는 문제임에도 입력이 100글자 까지라는 사실을 뒤늦게 알아서 한 번 헤맸다. 그리고 숫자 앞에 붙은 0을 제거할 때, 맨 뒤 숫자가 0이라면 0으로 반환 해버리는 잘못된 설계를 디버깅을 하며 찾아냈다. (0200 같이 0으로 끝나는 수를 0으로 반환) 항상 느끼지만 문자열은 꽤 섬세해서 문자열 관련 숙련도가 얼마나 있는지가 꽤 중요한 것 같다. 시간은 두 번째 푸는 것 치고 오래 걸렸지만 코드 자체는 이전 보다 깔끔히 짜여졌다고 생각한다.

 

 

#include<bits/stdc++.h>
using namespace std;

int n;
string temp;
vector<string> str, ret;
string number = "0123456789";

//문자열 숫자 비교 함수
bool cmp(string a, string b) {
	if (a.size() == b.size()) {
		for (int i = 0; i < a.size(); i++) {
			if (a[i] != b[i]) return a[i] < b[i];
		}
	}

	return a.size() < b.size();

}

//숫자 앞에 붙은 0을 제거, 혹은 0은 0으로
void rez(string* s) {
	int idx = 0;
	for (int i = 0; i < (*s).size(); i++) {
		if ((*s)[i] != '0') break;
		else (*s).erase(0, 1);
		i = -1;
	}
	if ((*s).empty()) (*s) = "0";
	return;
}

//문자열 한 줄에서 숫자를 찾아 vector<string> ret에 대입
void find_num(string s) {
	string token = "";
	bool flag = false;
	char _prev = 'a';
	for (int i = 0; i < s.size(); i++) {
		if (number.find(_prev) != string::npos && number.find(s[i]) == string::npos) {
			rez(&token);
			ret.push_back(token);
			token = "";
		}
		else if (number.find(s[i]) != string::npos) token += s[i];
		_prev = s[i];
	}
	if (token != "") {
		rez(&token);
		ret.push_back(token);
	}
	return;
}

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

	//입력
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> temp;
		str.push_back(temp);
	}

	//매 줄마다 문자열 검사
	for (int i = 0; i < str.size(); i++) {
		find_num(str[i]);
	}

	//문자열 숫자 정렬
	sort(ret.begin(), ret.end(), cmp);

	//출력
	for (int i = 0; i < ret.size(); i++) {
		cout << ret[i] << "\n";
	}

	return 0;
}

 

'알고리즘 : C++ > BaekJoon' 카테고리의 다른 글

백준 16234번 - 인구 이동  (0) 2023.05.25
백준 2589번 - 보물섬  (0) 2023.05.25
백준 15686번 - 치킨 배달  (0) 2023.05.23
백준 4659번 - 비밀번호 발음하기  (0) 2023.05.23
백준 2910번 - 빈도 정렬  (1) 2023.05.20

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

 

15686번: 치킨 배달

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

www.acmicpc.net

 

풀이 과정

 치킨집 개수 중 m개 를 뽑는 치킨집 조합 중 최소 '치킨 거리'를 구하는 문제이다. 치킨 거리는 치킨집에서 집까지 최소 이동 거리의 합을 말한다. 먼저 조합을 구현해서 m개의 치킨집을 구한다. 그리고 해당 조합의 치킨집이 존재할 때 '최소 치킨 거리'를 구해준다. 최소 치킨 거리를 구할 때, 치킨집마다 BFS를 수행해주며 최단 거리를 구했고, ret_mp라는 배열에 최댓값으로 초기화한 뒤 집이 있는 좌표의 최단 치킨 거리를 갱신해줬다. 치킨집마다 모든 BFS 수행이 끝나면 sum_dis() 함수를 수행해서 ret_mp 배열 중 hlist() (집 좌표 벡터)에 있는 값들을 더해서 뽑힌 치킨집 조합일 때, 치킨 거리를 구했다. 그리고 해당값을 ret과 비교해주며 최종적인 답을 구했다.

 

느낀 점

 어제 나름 시간을 많이 쏟았는데 잘 풀지 못한 문제였다. 분명 머릿속으로 어떻게 풀어야할지는 알겠는데, 구현이 잘 되지 않았다. 친구들을 만나는 와중에도, 집에 돌아와서도 계속 머릿속에서 나를 괴롭혔다. 그러다 문득 자기 전 드는 생각이 이 문제를 풀 때, 머릿속에 단계적인 흐름도가 나와 있었다. 하지만 소스 코드로 구현할 때는 기능을 분할하지 않고 한번에 구현 하다 보니 미숙한 코딩 실력에 흐름이 꼬였던 것이라는 생각이 들어, 오늘 아침에는 최대한 함수들을 나눠서 구현해봤고, 생각보다 금세 문제를 풀 수 있었다. 구현을 할 때 최대한 짧은 소스 코드로 구현하려고 하기 보다 내 생각을 함수 단위로 쪼개서 잘 표현하는 편이 구현하기에도 다른 사람이 보기에도 용이할 것 같다.

 

  중간에 답을 볼까 고민도 했지만 답을 보기에는 풀 수 있다는 느낌이 드는 문제였다. 또 사람들이 문제를 풀 때, 어지간한 구현 문제는 끝까지 고민해보고 DP는 답을 빨리 보는 편이라고 한다. 이건 내가 매달려서 해결할 수 있는 문제라고 생각했다. 답을 보는 것 자체가 나쁘다고 생각하지 않는다. 내가 알아야 될 사전 지식을 알지 못한 상태에서 문제를 접해 너무 큰 시간이 소요된다면, 내가 문제를 고민하며 얻는 사고 능력이라는 이점을 넘는 임계점이 있을 것이다. 문제를 어느 정도 고민해보고 이것은 '답을 보고 빠르게 습득해야하는 것'인지, '고민해보고 사고 능력을 훈련해야하는 것'인지 판단하는 능력이 필요하다. 이 문제를 끝까지 풀어보기를 잘했다는 생각이 든다.

 

 

 

#include<bits/stdc++.h>
using namespace std;

const int NMX = 50, MMX = 13;
int n, m, cnt, ret = NMX * NMX;
int mp[NMX][NMX], ret_mp[NMX][NMX];
int my[4] = {1, 0, -1, 0}, mx[4] = {0, 1, 0, -1};
vector<pair<int, int>> clist, hlist, cl;

//집마다 최소 거리 합(해당 조합일 때, 치킨 거리)
int sum_dis() {
	int sum = 0;
	for (int i = 0; i < hlist.size(); i++) sum += ret_mp[hlist[i].first][hlist[i].second];
	return sum;
}

//치킨집부터 BFS수행하며 최솟값 갱신
void bfs(int y, int x) {
	int visited[NMX][NMX];
	fill(&visited[0][0], &visited[0][0] + NMX * NMX, 0);
	queue<pair<int, int >> q;
	q.push({ y, x });
	visited[y][x] = 1;
    //cout << "in bfs\n";
	do {
		for (int i = 0; i < 4; i++) {
			//cout << "in dir\n";
			int ny = q.front().first + my[i];
			int nx = q.front().second + mx[i];
			if (ny < 0 || ny >= n || nx < 0 || nx >= n || visited[ny][nx])continue;
		    visited[ny][nx] = visited[q.front().first][q.front().second] + 1;
			if (mp[ny][nx] == 1) {
				//cout << "ny, nx : " << ny << ", " << nx << "\n";
				//cout << "visited[ny][nx] : " << visited[ny][nx] << "\n";
				ret_mp[ny][nx] = min(ret_mp[ny][nx], visited[ny][nx] - 1);
			}
			q.push({ ny,nx });
		}
		q.pop();
	} while (q.size());
	return;
}

//해당 조합일 때 최소 치킨거리
int ck_dis(vector<pair<int, int>>* cl) {
	int min_ret = 0;
	//치킨집에서 bfs 시작
	fill(&ret_mp[0][0], &ret_mp[0][0] + NMX * NMX, 2 * NMX + 1); //최솟값을 찾기위한 초기화
	for (int i = 0; i < (*cl).size(); i++) bfs((*cl)[i].first, (*cl)[i].second);
	//집마다 치킨거리 더하기
	min_ret = sum_dis();
	return min_ret;
}

//치킨집 개수 중 m개 조합
void combi(vector<pair<int, int>> * cl, int start) {
	//cl.sizE()개 중 r개 조합으로 경우의 수
	if ((*cl).size() == m) {
		//치킨집 조합 테스트
		/*
		cout << "조합테스트\n";
		for (int i = 0; i < cl->size(); i++) cout <<  "(" << (*cl)[i].first << "," << (*cl)[i].second << ")  ";
		cout << "\n";
		*/
		int temp = ck_dis(cl);
		ret = min(temp, ret);
		return;
	}

	for (int i = start + 1; i < clist.size(); i++) {
		cl->push_back(clist[i]);
		//cout << "push : " << clist[i].first << ", " << clist[i].second << "\n";
		combi(cl, i);
		//cout << "pop : " << (*cl)[cl->size() - 1].first << ", " << (*cl)[cl->size() - 1].first << "\n";
		cl->pop_back();
	}
	return;
}

//입력
void input() {
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> mp[i][j];
			if (mp[i][j] == 2) {
				clist.push_back({ i, j }); //치킨 좌표
				//cout << "push i j c  : " << i << ", " << j << "\n";
				mp[i][j] = 0;
			}
			else if (mp[i][j] == 1) hlist.push_back({ i, j }); //집 좌표
		}
	}
	return;
}

//출력
void output() {
	cout << ret << "\n";
}

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

	combi(&cl, -1);

	output();

	return 0; 
}

'알고리즘 : C++ > BaekJoon' 카테고리의 다른 글

백준 2589번 - 보물섬  (0) 2023.05.25
백준 2870번 - 수학 숙제  (0) 2023.05.23
백준 4659번 - 비밀번호 발음하기  (0) 2023.05.23
백준 2910번 - 빈도 정렬  (1) 2023.05.20
백준 2828번 - 사과 담기 게임  (0) 2023.05.20

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

 

4659번: 비밀번호 발음하기

좋은 패스워드를 만드는것은 어려운 일이다. 대부분의 사용자들은 buddy처럼 발음하기 좋고 기억하기 쉬운 패스워드를 원하나, 이런 패스워드들은 보안의 문제가 발생한다. 어떤 사이트들은 xvtp

www.acmicpc.net

 

풀이 과정

 문자열을 조건에 맞춰 검사하고 조건에 부합하는지 하지 않는지를 출력하는 문제이다. 문자열에 모음이 있는지 cmps 문자열 "aeiou"에서 s[i]를 찾으며 판별한다. flag를 이용해 자음과 모음이 3개 연속으로 오는지 판단하고 만약 3개가 연속된다면 con을 false로 바꾸고 for문을 종료한다. 그리고 e와 o를 제외하고 연속으로 같은 두 글자가 오는지 판별한다. excep 문자열 "eo"에 포함된 문자인지 확인하고 포함되지 않은 문자가 연속해서 위치한다면 con을 false로 바꾸고 for문을 종료한다. 최종적으로 con 값에 맞춰 해당 문자열이 조건에 부합하는지 출력하게 된다.

 

느낀 점

 문자열을 다루는 건 쉬워보이지만 종종 뇌정지가 올 때가 있다. 이번 문제를 통해서 문자열을 검사하는 기법에 대해 생각해 볼 수 있었다. 예외로 두는 문자들을 cmps와 excep과 같은 문자열에 담아 표현하는게, 조건이 변경 되었을 때 더 유연하게 사용할 수 있을거라고 생가한다.

 

#include<bits/stdc++.h>
using namespace std;
string s;
string cmps = "aeiou";
string excep = "eo";
int main() {
	while (1) {
		cin >> s;
		if (s == "end") break;
		bool aorb = false, con = false;
		int check_a = 0, check_b = 0;
		for (int i = 0; i < s.size(); i++) {
			bool aflag = false;
			//모음 유무 판별
			if (cmps.find(s[i]) != string::npos) {
				con = true;
				aflag = true;
				check_a++;
			}
			else {
				aflag = false;
				check_b++;
			}
			//자음 모음 연속 3개 이상 판별
			if (check_a * check_b != 0) {
				if (aflag) { check_a = 1; check_b = 0; }
				else { check_a = 0; check_b = 1; }
			}
			else if (check_a > 2 || check_b > 2) {
				con = false;
				break;
			}
			//연속 두글자 판별 ee oo 제외
			if (i < s.size() - 1 && s[i] == s[i + 1]) {
				if (excep.find(s[i]) == string::npos) {
					con = false;
					break;
				}
			}
		}
		if (con) cout << "<" << s << "> is acceptable.\n";
		else cout << "<" << s << "> is not acceptable.\n";
	}
	return 0;
}

'알고리즘 : C++ > BaekJoon' 카테고리의 다른 글

백준 2870번 - 수학 숙제  (0) 2023.05.23
백준 15686번 - 치킨 배달  (0) 2023.05.23
백준 2910번 - 빈도 정렬  (1) 2023.05.20
백준 2828번 - 사과 담기 게임  (0) 2023.05.20
백준 1992번 - 쿼드트리  (0) 2023.05.20

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

 

2910번: 빈도 정렬

첫째 줄에 메시지의 길이 N과 C가 주어진다. (1 ≤ N ≤ 1,000, 1 ≤ C ≤ 1,000,000,000) 둘째 줄에 메시지 수열이 주어진다.

www.acmicpc.net

 

풀이 과정

 여러 개의 수가 주어질 때, 출현 빈도의 내림차순으로, 동일한 경우 먼저 출현한 순으로, 수를 정렬하는 문제이다. map<ll, int> 와 vector<pair<ll, int>>를 이용해서 풀었다. map에 input값을 넣으면 vector의 인덱스 번호가 나오고 해당 번호에서 입력값과 출현 횟수를 볼 수 있는 방식이다. 동일한 요소가 들어 왔을 때, map을 사용하기 때문에 더 빠르게 접근할 수 있지 않을까 생각했다. 그리고 stable_sort()와 cmp() 함수를 구현해 pair<ll, int>의 second값을 기준으로 내림차순 정렬로 만들었다. stable_sort() (병합 정렬)는 일반 sort() (퀵 정렬)와 다르게 동일한 값을 가진 요소에 대해 기존 순서를 보장함으로 동일한 출현 횟수를 가질 경우 먼저 출현한 수를 출력할 수 있다.

 

느낀 점

 map<ll, int>과  vector<pair<ll ,int>>로 감을 잡는데 어려웠다. C가 크지만 N이 1000으로 작기 때문에 굳이 map을 사용하지 않고 두 개의  vector를 이용해서 풀 수도 있었다. 또 sort()와 stable_sort()의 차이를 알 수 있는 좋은 경험이었다. 만약 sort() 함수의 동일한 값에 대한 기존 순서를 유지하고 싶다면 인덱스 정보를 추가로 compare() 함수에 추가해준다면 될 것이다.

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

map<ll, int> mp;
ll n, c, temp;
vector<pair<ll, int>> cnt;

bool cmp(pair<ll, int> a, pair<ll, int> b) {
	return a.second > b.second;
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	//입력
	cin >> n >> c;
	cnt.push_back({ 0, 0 }); // 1부터 탐색하기 위한 더미
	//수행
	for (int i = 0; i < n; i++) {
		cin >> temp;
		if (mp[temp] == 0) {//0은 더미
			cnt.push_back({ temp, 1 }); //cnt에 숫자와 카운트 기록
			mp[temp] = cnt.size() - 1; //mp에 해당 숫자 정보로 가는 cnt 인덱스 기록
		}
		else {
			cnt[mp[temp]].second++; //map에서 해당 숫자의 인덱스를 찾고 cnt++
		}
	}
	//정렬
	stable_sort(cnt.begin(), cnt.end(), cmp);
	//출력
	for (int i = 0; i < cnt.size(); i++) {
		for (int j = 0; j < cnt[i].second; j++) cout << cnt[i].first << " ";
	}

	return 0;
}

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

 

2828번: 사과 담기 게임

상근이는 오락실에서 바구니를 옮기는 오래된 게임을 한다. 스크린은 N칸으로 나누어져 있다. 스크린의 아래쪽에는 M칸을 차지하는 바구니가 있다. (M<N) 플레이어는 게임을 하는 중에 바구니를

www.acmicpc.net

 

풀이 과정

 주어진 N 크기의 1차원 배열에 사과가 떨어지면 바구니를 최소한으로 옮겨 사과를 받고 이동 거리를 구하는 문제이다. 일단 바구니를 하나의 객체로 생각하고 basket이라는 pair<int, int> 변수에 바구니의 시작점과 끝점을 담았다. 그리고 move_basket()에 사용할 바구니의 주소와 사과가 떨어지는 지점을 주고 사과를 담기 위한 바구니의 최소 이동거리를 반환했다. ret에 사과가 떨어질 때마다 이동한 거리를 더해주면서 해결했다.

 

느낀 점

 굳이 객체로 만들어 풀지 않아도 됬을거 같지만 이렇게 하니 손쉽게 풀렸다. 단, 바구니의 경계값을 잘 체크해야하고 이동 거리를 구할 때는 절댓값을 구해야 한다는 사실을 명심하자.

 

#include<bits/stdc++.h>
using namespace std;

int n, m, k, temp, ret;
pair<int, int> basket; //start, end
//(이동시킬 바구니, 사과 도착 지점)으로 이동거리 반환
int move_basket(pair<int, int> *bas, int des) {
	//바구니를 이동 불필요
	if (des >= bas->first && des <= bas->second) return 0;
	int move_dis = 0;
	//바구니를 이동 필요, 이동거리 계산
	if (des < bas->first) move_dis = des - bas->first;
	else if (des > bas->second) move_dis = des - bas->second;
	//바구니 이동
	bas->first += move_dis;
	bas->second += move_dis;

	return abs(move_dis);
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	//입력
	cin >> n >> m >> k;
	//초기화
	basket = { 1, m };
	//수행
	for (int i = 0; i < k; i++) {
		cin >> temp;
		ret += move_basket(&basket, temp);
	}
	//출력
	cout << ret;

	return 0;
}

'알고리즘 : C++ > BaekJoon' 카테고리의 다른 글

백준 4659번 - 비밀번호 발음하기  (0) 2023.05.23
백준 2910번 - 빈도 정렬  (1) 2023.05.20
백준 1992번 - 쿼드트리  (0) 2023.05.20
백준 2583번 - 영역 구하기  (0) 2023.05.20
백준 2468번 - 안전 영역  (0) 2023.05.20

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

 

1992번: 쿼드트리

첫째 줄에는 영상의 크기를 나타내는 숫자 N 이 주어진다. N 은 언제나 2의 제곱수로 주어지며, 1 ≤ N ≤ 64의 범위를 가진다. 두 번째 줄부터는 길이 N의 문자열이 N개 들어온다. 각 문자열은 0 또

www.acmicpc.net

 

풀이 과정

 전체 map 탐색하며 조건을 만족하면 출력, 아니라면 만족할 때까지 분할하는 문제이다. 입력을 받고 qt()라는 재귀 함수를 작성해서 문제를 풀었다. qt() 함수는 먼저 지정된 영역 (x, y) 부터 (x + nn, y + nn)까지 값이 동일한지 검사한다. 동일하다면 범위 내 mp 요소를 하나 출력한다. 그러나 동일하지 않다면 mp를 4부분으로 분할해서 다시 검사한다. 분할 될 때는 앞에 '(', 분할 된 모든 qt()가 종료되면 ')'를 출력한다.

 

 느낀 점

 처음 풀 때는 어려움을 많이 느꼈는데, 다시 풀어보니 간단히 풀렸다. 분할 정복이란 무엇인지 직관적으로 느낄 수 있었다. 중간에 qt()를 4부분으로 분할할 때 y와 x를 하나 헷갈려 바꾸어 적었고, 디버깅하면서 탐색 범위를 좁히고 수정했다.

 

#include<bits/stdc++.h>
using namespace std;

int n;
char mp[65][65];

void qt(int y, int x, int nn) {
	int flag = false, _prev = mp[y][x];
	for (int i = y; i < y + nn; i++) {
		for (int j = x; j < x + nn; j++) {
			if (mp[i][j] != _prev) flag = true;
		}
	}
	//값이 동일하지 않다면 분할
	if (flag) {
		cout << "(";
		qt(y, x, nn / 2);
		qt(y, x + nn / 2, nn / 2);
		qt(y + nn / 2, x, nn / 2);
		qt(y + (nn / 2), x + (nn / 2), nn / 2);
		cout << ")";
	}//동일하면 출력
	else cout << mp[y][x];

	return;
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);
	//입력
	cin >> n;
	for (int i = 0; i < n; i ++) {
		for (int j = 0; j < n; j++) {
			cin >> mp[i][j];
		}
	}
	//수행 및 출력
	qt(0, 0, n);

	return 0;
}

'알고리즘 : C++ > BaekJoon' 카테고리의 다른 글

백준 2910번 - 빈도 정렬  (1) 2023.05.20
백준 2828번 - 사과 담기 게임  (0) 2023.05.20
백준 2583번 - 영역 구하기  (0) 2023.05.20
백준 2468번 - 안전 영역  (0) 2023.05.20
백준 1012번 - 유기농 배추  (0) 2023.05.20

+ Recent posts