문제 링크


www.acmicpc.net/problem/17070

 

17070번: 파이프 옮기기 1

유현이가 새 집으로 이사했다. 새 집의 크기는 N×N의 격자판으로 나타낼 수 있고, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 (r, c)로 나타낼 수 있다. 여기서 r은 행의 번호, c는 열의

www.acmicpc.net

접근 방법


어렵지 않게 풀 수 있는 문제였으나, DP테이블을 설계 할 때 고려해야 할 점이 하나 있습니다.

위의 그림은 모두 같은 위치에 있지만, 해당 위치에서 파이프의 방향에 따라 앞으로 진행될 경우가 모두 다르다는 것을 생각 할 수 있다. 즉 DP테이블을 dp[가로][세로][3]으로 생각하여 문제를 해결하면 된다.  

 

소스 코드


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

using namespace std;

int n;
int map[16][16];
int dp[16][16][3];

bool isPossible(int x, int y, int dir) {
	if (x >= n || y >= n)
		return false;
	if (dir == 0 || dir == 1) 
		if (map[x][y] == 1) 
			return false;
	if (dir == 2)
		if (map[x][y] == 1 || map[x - 1][y] == 1 || map[x][y - 1] == 1)
			return false;
	return true;
}

int solve(int x, int y, int dir) { //dir 0 오른, 1 아래, 2 대각

	if (!isPossible(x, y, dir)) 
		return 0;
	
	int &ref = dp[x][y][dir];

	if (ref != -1) 
		return ref;
	ref = 0;

	if (x == n - 1 && y == n - 1) 
		return ref = 1;

	if (dir == 0) { // 오른쪽일때
			ref += solve(x, y + 1, 0); //오른쪽 그대로
			ref += solve(x + 1, y + 1, 2); //대각으로
	}
	else if (dir == 1) { // 아래일때
			ref += solve(x + 1, y, 1); //아래 그대로
			ref += solve(x + 1, y + 1, 2); //대각으로
	}
	else { //대각 일 때
			ref += solve(x, y + 1, 0); //오른쪽로 가거나
			ref += solve(x + 1, y, 1); //아래로 가거나
			ref += solve(x + 1, y + 1, 2); //대각으로 가거나
	}
	return ref;
}

int main() {

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

	cin >> n;

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> map[i][j];
		}
	}
	memset(dp, -1, sizeof(dp));
	cout << solve(0,1,0);
}

개발 환경 : VS2017

질문, 지적 환영합니다!

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

[C++] 백준 17136 색종이 붙이기  (0) 2021.01.02
[C++] 백준 9470 Strahler 순서  (0) 2020.09.25
[C++] 백준 17135 캐슬디펜스  (0) 2020.09.18
[C++] 백준 5670 휴대폰 자판  (0) 2020.09.10
[C++] 백준 10266 시계 사진들  (0) 2020.09.09

문제 링크


www.acmicpc.net/problem/17135

 

17135번: 캐슬 디펜스

첫째 줄에 격자판 행의 수 N, 열의 수 M, 궁수의 공격 거리 제한 D가 주어진다. 둘째 줄부터 N개의 줄에는 격자판의 상태가 주어진다. 0은 빈 칸, 1은 적이 있는 칸이다.

www.acmicpc.net

접근 방법


 설계만 잘한다면 어렵지 않게 풀 수 있는 문제였습니다.

 

1. 궁수가 3명 필요하므로 3명을 세울 수 있는 조합을 모두 찾는다.

2. 조합이 완성되었을 때, 궁수 각자의 표적을 찾는다.

3. 표적을 제거 후 다음 턴을 진행한다.  

 

1. 궁수가 3명 필요하므로 3명을 세울 수 있는 조합을 모두 찾는다.

void comb(int cnt, int start, vector<int> &archers) {
	if (cnt == 3) {
		int result = 0;
		copyTmp();
		for (int i = 0; i < n; i++) {
			result += thisTurn(archers);
		}
		ans = max(ans, result);
		return;
	}
	for (int i = start + 1; i < m; i++) { //m이 가로 길이
		archers.push_back(i);
		comb(cnt + 1, i, archers);
		archers.pop_back();
	}
}

backtracking을 사용하며 가능한 모든 궁수 3명의 조합을 찾을 수 있습니다.

조합이 완성되면 게임을 진행하며 총 몇명의 적(result)이 제거 되었는지 카운트 합니다.

thisTurn()은 한 턴에서 제거된 적을 계산하며, 한 게임은 세로길이는 n턴이 지나야 게임이 끝나므로 n번 반복합니다.

 

2. 조합이 완성되었을 때, 궁수 각자의 표적을 찾는다.

int thisTurn(vector<int> &archers) {

	int cnt = 0;

	vector <pair<int, int>> targets;
	for (int i = 0; i < archers.size(); i++) {
		pair<int, int> target = decideTarget(archers[i]);
		if (target.first != 999 && target.second != 999) {
			targets.push_back(target);
		}
	}
	for (int i = 0; i < targets.size(); i++) {
		pair<int, int> target = targets[i];
		if (tmp[target.first][target.second] == 1) {
			tmp[target.first][target.second] = 0;
			cnt++;
		}
	}

	for (int i = n - 2; i >= 0; i--)
		for (int j = 0; j < m; j++) 
			tmp[i + 1][j] = tmp[i][j];
	for (int j = 0; j < m; j++)
		tmp[0][j] = 0;

	return cnt;
}

궁수 한명의 표적을 찾는 일은 decideTarget()에서 진행됩니다. 제거할 모든 적을 vector<pair<int, int>> targets에 저장한 뒤 적을 제거하며 cnt를 카운트합니다. 이렇게 한턴이 종료 되면 적들을 한칸 씩 밑으로 이동 시킵니다.

 

3. pair<int, int> decideTarget(int archer)

pair<int, int> decideTarget(int archer) {
	
	bool visited[15][15] = { false };
	pair<int, int> nextdir[3] = { {-1,0}, {0,1}, {0,-1} };
	pair<int, int> ret = { 999, 999 };
	queue<coord> q;
	int dist = 999;

	q.push({ n - 1, archer, 1 });

	while (!q.empty()) {

		coord now = q.front();
		q.pop();

		if ((dist != 999 && dist < now.dist) || now.dist > d) //이미 표적을 잡았는데 더 멀리 갈때, 공격 범위를 벗어 낫을 때
			break;

		if (tmp[now.x][now.y] == 1) {
			if (dist >= now.dist) {
				dist = now.dist;
				if(ret.second > now.y) //기존 표적보다 더 왼쪽에 있을 때
					ret = { now.x, now.y };
			}
		}

		for (int j = 0; j < 3; j++) {
			int next_x = now.x + nextdir[j].first;
			int next_y = now.y + nextdir[j].second;
			if (0 <= next_x && next_x < n && 0 <= next_y && next_y < m) {
				if (!visited[next_x][next_y]) {
					visited[next_x][next_y] = true;
					q.push({ next_x, next_y, now.dist + 1 });
				}
			}
		}
	}
	return ret;
}

 궁수 한명이 표적을 찾는 함수입니다.  가장 짧은 거리의 적을 표적으로 삼는 조건이 있기 때문에 DFS보다 BFS가 적합하다고 판단하였습니다. 

소스 코드


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

using namespace std;

int map[15][15];
int tmp[15][15];
int n, m, d;
int ans = 0;

typedef struct coord {
	int x;
	int y;
	int dist;
};

void copyTmp() {
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			tmp[i][j] = map[i][j];
}

pair<int, int> decideTarget(int archer) {
	
	bool visited[15][15] = { false };
	pair<int, int> nextdir[3] = { {-1,0}, {0,1}, {0,-1} };
	pair<int, int> ret = { 999, 999 };
	queue<coord> q;
	int dist = 999;

	q.push({ n - 1, archer, 1 });

	while (!q.empty()) {

		coord now = q.front();
		q.pop();

		if ((dist != 999 && dist < now.dist) || now.dist > d) //이미 표적을 잡았는데 더 멀리 갈때, 공격 범위를 벗어 낫을 때
			break;

		if (tmp[now.x][now.y] == 1) {
			if (dist >= now.dist) {
				dist = now.dist;
				if(ret.second > now.y) //기존 표적보다 더 왼쪽에 있을 때
					ret = { now.x, now.y };
			}
		}

		for (int j = 0; j < 3; j++) {
			int next_x = now.x + nextdir[j].first;
			int next_y = now.y + nextdir[j].second;
			if (0 <= next_x && next_x < n && 0 <= next_y && next_y < m) {
				if (!visited[next_x][next_y]) {
					visited[next_x][next_y] = true;
					q.push({ next_x, next_y, now.dist + 1 });
				}
			}
		}
	}
	return ret;
}

int thisTurn(vector<int> &archers) {

	int cnt = 0;

	vector <pair<int, int>> targets;
	for (int i = 0; i < archers.size(); i++) {
		pair<int, int> target = decideTarget(archers[i]);
		if (target.first != 999 && target.second != 999) {
			targets.push_back(target);
		}
	}
	for (int i = 0; i < targets.size(); i++) {
		pair<int, int> target = targets[i];
		if (tmp[target.first][target.second] == 1) {
			tmp[target.first][target.second] = 0;
			cnt++;
		}
	}

	for (int i = n - 2; i >= 0; i--)
		for (int j = 0; j < m; j++) 
			tmp[i + 1][j] = tmp[i][j];
	for (int j = 0; j < m; j++)
		tmp[0][j] = 0;

	return cnt;
}

void comb(int cnt, int start, vector<int> &archers) {
	if (cnt == 3) {
		int result = 0;
		copyTmp();
		for (int i = 0; i < n; i++) {
			result += thisTurn(archers);
		}
		ans = max(ans, result);
		return;
	}
	for (int i = start + 1; i < m; i++) { //m이 가로 길이
		archers.push_back(i);
		comb(cnt + 1, i, archers);
		archers.pop_back();
	}
}

void solve() {
	vector<int> archers;
	comb(0, -1, archers);
}

int main() {

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

	cin >> n >> m >> d;

	for (int i = 0; i < n; i++) 
		for (int j = 0; j < m; j++) 
			cin >> map[i][j];
		
	solve();
	cout << ans;
}

개발 환경 : VS2019

질문, 지적 환영합니다!

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

[C++] 백준 9470 Strahler 순서  (0) 2020.09.25
[C++] 백준 17070 파이프 옮기기 1  (0) 2020.09.18
[C++] 백준 5670 휴대폰 자판  (0) 2020.09.10
[C++] 백준 10266 시계 사진들  (0) 2020.09.09
[C++] 백준 1300 k번째 수  (0) 2020.09.07

문제 링크


www.acmicpc.net/problem/5670

 

5670번: 휴대폰 자판

휴대폰에서 길이가 P인 영단어를 입력하려면 버튼을 P번 눌러야 한다. 그러나 시스템프로그래밍 연구실에 근무하는 승혁연구원은 사전을 사용해 이 입력을 더 빨리 할 수 있는 자판 모듈을 개발

www.acmicpc.net

접근 방법


trie의 구조를 자식을 mapping할 수 있는 자기가신의 자료형과, 단어의 마지막인지 check하는 isFinished로 만들었다.

typedef struct Trie {
	bool isFinished;
	map<char, Trie> child;
};

trie의 삽입 과정은 어렵지 않으므로 자세한 설명없이 가장 밑에 소스코드로 첨부하겠다. 

문제를 해결해보자, 언제 우리는 타자를 쳐야할까? 

1. 트라이의 분기가 생길 때

2. 이미 딕셔너리에 포함된 단어를 포함하는 더 긴 단어를 입력할 때이다. 

 

아래 그림은 hell, hello, heaven, goodbye로 만든 trie구조이다.

1번은 아래 소스코드의 방법으로 확인 가능하다. 

 if (node.child.size() > 1 || idx == 0) {
	return inputCount(node.child[word[idx]], word, idx + 1) + 1;
}

(idx==0)인 경우는 현재 루트의 위치에 있을때이고, (node.child.size() > 1)인 경우는 자식이 최소 2개 이상일 때, 즉 분기점 이라는 것이다. 

 

2번은 아래 소스코드의 방법으로 확인 가능하다.

if (node.child.size() == 1 && node.isFinished == true) {
	return inputCount(node.child[word[idx]], word, idx + 1) + 1;
}

 (node.isFinished == true) && (node.child.size() == 1) -> 현재 탐색한 단어보다 더 긴 단어를 찾아갈 것이고, 현재 위치에서 끝난 단어가 있다는 뜻이다. 

소스 코드


#include <iostream>
#include <string>
#include <vector>
#include <map>

using namespace std;

typedef struct Trie {
	bool isFinished;
	map<char, Trie> child;
};

void insert(Trie &node, string &word, int idx) {

	if (idx == word.size()) {
		node.isFinished = true;
		return;
	}
	if (node.child.count(word[idx]) == 0) {
		node.child[word[idx]] = Trie();
		node.child[word[idx]].isFinished = false;
	}
	insert(node.child[word[idx]], word, idx + 1);
}

int inputCount(Trie &node, string &word, int idx) {
	if (idx == word.size()) {
		return 0;
	}
	else if (node.child.size() > 1 || idx == 0) {
		return inputCount(node.child[word[idx]], word, idx + 1) + 1;
	}
	else if (node.child.size() == 1 && node.isFinished == true) {
		return inputCount(node.child[word[idx]], word, idx + 1) + 1;
	}
	else
		return inputCount(node.child[word[idx]], word, idx + 1);
}

int main() {

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

	int n;

	while (cin >> n) {

		vector <string> words;
		int sum = 0;

		Trie trie;
		for (int i = 0; i < n; i++) {
			string word;
			cin >> word;
			words.push_back(word);
			insert(trie, word, 0);
		}
		for (int i = 0; i < words.size(); i++) {
			sum += inputCount(trie, words[i], 0);
		}
		cout << fixed;
		cout.precision(2);
		cout << double(sum)/words.size() << "\n";
	}
}

개발 환경 : Visual Studio 2019

질문, 덧글, 지적 환영합니다.

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

[C++] 백준 17070 파이프 옮기기 1  (0) 2020.09.18
[C++] 백준 17135 캐슬디펜스  (0) 2020.09.18
[C++] 백준 10266 시계 사진들  (0) 2020.09.09
[C++] 백준 1300 k번째 수  (0) 2020.09.07
[C++] 백준 1005 ACM craft  (0) 2020.09.06

문제 링크


www.acmicpc.net/problem/10266

 

10266번: 시계 사진들

상근이는 보통의 시계와는 다른 독특한 시계 사진 두장이 있습니다. 시계는 n개의 동일한 길이와 목적을 가진 시계 바늘들을 가지고 있습니다. 애석하게도 시계의 숫자들은 희미해져 각 시계 바

www.acmicpc.net

접근 방법


 

 문제에서 2개의 시계가 주어진다. 정답을 찾기위해 우리는 시계2를 적절히 회전하여 시계1과 일치하는지 확인을 해야한다. 이것을 어떻게 풀어 갈 수 있을까? 

시계는 36만의 각도가 존재하므로 36만개의 문자열로 생각해보자. 이 때 시침이 가리키는 각도는 1, 아니면 0이라고 표기를 하자.

예를 들어 총 5개의 각도가 존재하고 ,각도1, 각도3을 시침이 가리킨다면

1 0 1 0 0

이러한 모양일 것이다. 

 

그럼 시계2를 회전 시켜 시계 1과 일치하게 하는 것은 어떻게 해야할까? 아래 그림을 보자!

시계 2를 회전 시킨다는 것은 위의 그림과 같이 시계를 오른쪽으로 이동시키는 과정이다. 또한 시계라는 특성상 끝 지점을 지나면 다시 처음으로 순환되는 구조이므로 시계1 두개를 이어 붙여버리면 순환구조를 탐색하는데 전혀 문제가 없게된다. 

 KMP알고리즘을 공부했다면 시계1을 두 개 이어붙인 것을 전체 TEXT, 시계2를 Pattern으로 사용하여 해결할 수 있을 것이다. KMP알고리즘을 잘 모른다면 countrysides.tistory.com/27를 참고하도록 하자.   

소스코드


#include <iostream>
#include <vector>
#include <string>
#define MAX 360000

using namespace std;

vector<int> getpi(vector<bool> pattern) {

	vector<int> pi(pattern.size(), 0);
	int j = 0;

	for (int i = 1; i < pattern.size(); i++) {
		while (j > 0 && pattern[i] != pattern[j]) {
			j = pi[j - 1];
		}
		if (pattern[i] == pattern[j]) {
			pi[i] = ++j;
		}
	}
	return pi;
}

string kmp(vector<bool> text, vector<bool> pattern) {

	vector<int> pi = getpi(pattern);
	int j = 0;

	for (int i = 0; i < text.size(); i++) {
		while (j > 0 && text[i] != pattern[j]) {
			j = pi[j - 1];
		}
		if (text[i] == pattern[j]) {
			if (j == pattern.size() - 1) {
				return "possible\n";
			}
			else
				j++;
		}
	}
	return "impossible\n";
}

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

	int n;
	vector<bool> clock1(MAX * 2, 0); // text
	vector<bool> clock2(MAX, 0);     // pattern

	cin >> n;

	for (int i = 0; i < n; i++) {
		int num;
		cin >> num;
		clock1[num] = clock1[MAX+num] = 1;
	}
	for (int i = 0; i < n; i++) {
		int num;
		cin >> num;
		clock2[num] = 1;
	}

	cout << kmp(clock1, clock2);
}

개발 환경 : Visual Studio 2019

질문, 덧글, 지적 환영합니다.

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

[C++] 백준 17135 캐슬디펜스  (0) 2020.09.18
[C++] 백준 5670 휴대폰 자판  (0) 2020.09.10
[C++] 백준 1300 k번째 수  (0) 2020.09.07
[C++] 백준 1005 ACM craft  (0) 2020.09.06
[C++] 백준 2252 줄 세우기  (0) 2020.09.05

문제 링크


www.acmicpc.net/problem/1300

 

1300번: K번째 수

세준이는 크기가 N×N인 배열 A를 만들었다. 배열에 들어있는 수 A[i][j] = i×j 이다. 이 수를 일차원 배열 B에 넣으면 B의 크기는 N×N이 된다. B를 오름차순 정렬했을 때, B[k]를 구해보자. 배열 A와 B��

www.acmicpc.net

접근 방법


 문제를 푸는 과정은 어렵지 않았으나, 문제를 푸는 방법을 떠올리는 것이 어려운 문제였다.

크게 3단계로 나뉜다.

1. 임의의 수 mid를 잡는다.

2. mid보다 같거나 작은 숫자가 갯수 cnt를 찾는다.

3. cnt < k 라면 mid가 더 큰 수여야 한다는 뜻이고, cnt >= k 라면 더 작은 수 이거나 현재 조건을 만족한다는 의미이다. 

 

1번

 과정은 start=1, end=k, mid = (start+end)/2로 잡는다. end = k인 이유는 k보다 작은 숫자는 적어도 k개 보다 많거나 같기 때문이다.

 

2번

 cnt를 구하는 과정이 떠올리기에 어려울 수 있다. 하단의 코드를 보며 차근 차근 따라가보자

long long countNum(long long mid) {
	
	long long cnt = 0;
	for (int i = 1; i <= n; i++) {
		cnt += min( mid/i, n);
	}
	return cnt;
}

만약 n=4, mid = 8이라면

1 2 3 4
2 4 6 8
3 6 9 12
4 8 12 16

이러한 모양일 것이다.

즉, i행은 i*1, i*2, i*3, ..의 숫자들로 이루어져 있고, i행의 숫자들 중 mid보다 작거나 같은 숫자는

(i*j <= m)를 만족하는 j의 개수이고 이는 i*1, i*2, ..., i*j이므로, mid/i와 같은 값이 된다. 하지만 mid/i가 n을 초과할 경우

i행의 cnt=n이 될 것이다.

 

소스 코드


#include <iostream>
#include <algorithm>

using namespace std;

long long n, k;

long long countNum(long long mid) {
	
	long long cnt = 0;
	for (int i = 1; i <= n; i++) {
		cnt += min( mid/i, n);
	}
	return cnt;
}

long long search() {
	
	long long end = k;
	long long start = 1;
	long long result = 0;

	while (start <= end) {
		
		long long mid = (start + end) / 2;
		long long cnt = countNum(mid); //mid보다 작거나 같은 갯수

		if (cnt < k) {  
			start = mid + 1;
		}	
		else {
			result = mid;
			end = mid - 1;
		}
	}
	return result;
}

int main() {

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

	cin >> n >> k;
	cout << search();
}

개발 환경 : VS2019

질문, 지적 환영합니다!

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

[C++] 백준 5670 휴대폰 자판  (0) 2020.09.10
[C++] 백준 10266 시계 사진들  (0) 2020.09.09
[C++] 백준 1005 ACM craft  (0) 2020.09.06
[C++] 백준 2252 줄 세우기  (0) 2020.09.05
[C++] 백준 16637 괄호 추가하기  (0) 2020.09.05

문제 링크


www.acmicpc.net/problem/1005

 

1005번: ACM Craft

첫째 줄에는 테스트케이스의 개수 T가 주어진다. 각 테스트 케이스는 다음과 같이 주어진다. 첫째 줄에 건물의 개수 N 과 건물간의 건설순서규칙의 총 개수 K이 주어진다. (건물의 번호는 1번부�

www.acmicpc.net

접근 방법


 일단 문제를 보자마자 떠오른 것은 DP를 사용해야겠다는 것이다. 처음 top-down방식으로 해결하였으나, 재귀가 깊어져서 인지 메모리 제한에 걸렸다.  그래서 bottom-up방식을 사용하여 다시 해결했다.

 

동적계획법을 사용하는 핵심 코드이다.

for (int i = 0; i < map[now].size(); i++) {
  int next = map[now][i];
  if (--indegree[next] == 0) {
  	q.push(next);
  }
  dp[next] = max(dp[next], dp[now] + buildtime[next]);
}

예를 들어 a를 짓기 위해 b와 c 두 건물이 모두 지어져야 한다면, b와 c둘 중 짓는대 까지 걸리는 시간 중 큰 값을 사용하여 현재 건물을 짓는 시간을 더하여 갱신 해주었다. 이 부분만 잘 이해한다면 크게 어려울 것은 없을 것이다.

 

소스 코드


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

using namespace std;

int buildtime[1001];
int dp[1001];
int indegree[1001];
vector<int> map[1001];
int t, n, k, dest;

void init() {

	memset(buildtime, 0, sizeof(buildtime));
	memset(dp, 0, sizeof(dp));
	memset(indegree, 0, sizeof(indegree));
	for (int i = 0; i <= n; i++) {
		map[i].clear();
	}

	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		cin >> buildtime[i];
	}
	for (int i = 0; i < k; i++) {
		int a, b;
		cin >> a >> b;
		map[a].push_back(b);
		indegree[b]++;
	}
	cin >> dest;
}

int solve(int dest) {

	int result = 0;

	queue <int> q;
	for (int i = 1; i <= n; i++) 
		if (indegree[i] == 0) {
			q.push(i);
			dp[i] = buildtime[i];
		}

	while (!q.empty()) {
		
		int now = q.front();
		q.pop();

		if (now == dest) {
			result = dp[now];
			break;
		}

		for (int i = 0; i < map[now].size(); i++) {
			int next = map[now][i];
			if (--indegree[next] == 0) {
				q.push(next);
			}
			dp[next] = max(dp[next], dp[now] + buildtime[next]);
		}
	}

	return result;
}

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

	cin >> t;

	while (--t >= 0) {
		init();
		cout << solve(dest) << "\n";
	}
}

개발 환경 : VS2019

질문, 지적 환영합니다!

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

[C++] 백준 10266 시계 사진들  (0) 2020.09.09
[C++] 백준 1300 k번째 수  (0) 2020.09.07
[C++] 백준 2252 줄 세우기  (0) 2020.09.05
[C++] 백준 16637 괄호 추가하기  (0) 2020.09.05
[C++] 백준 1937 욕심쟁이판다  (0) 2020.09.04

문제 링크


www.acmicpc.net/problem/2252

 

2252번: 줄 세우기

첫째 줄에 N(1≤N≤32,000), M(1≤M≤100,000)이 주어진다. M은 키를 비교한 회수이다. 다음 M개의 줄에는 키를 비교한 두 학생의 번호 A, B가 주어진다. 이는 학생 A가 학생 B의 앞에 서야 한다는 의미이��

www.acmicpc.net

접근 방법


이 문제는 가장 기본적인 위상 정렬 문제이다. 

 

이 문제는 크게 3가지 부분으로 나눌 수 있습니다. 

 

1. 입력을 받음과 동시에 indegree체크

2. q에 초기값 담기

3. q의 값을 pop하며, 조건에 해당하는 값을 push

아래에서 자세한 설명을 하겠습니다.

 

1. 입력을 받음과 동시에 indegree체크

	for (int i = 0; i < m; i++) {
		int a, b;
		cin >> a >> b;
		v[a].push_back(b);
		indegree[b]++;
	}

 

위상정렬은 자신이 무엇인가를 하기 위해 다른 무엇인가가 완료되어야 할 수 있다?

즉, 어떤 일을 하는 순서를 찾는 알고리즘 입니다.

예를 들어, '그냥 기초'를 하기 위해선 완전 기초가 요구되어야 하며, '어려움'을 위해서는 '그냥 기초'가 수행되어야 하고,

'보통'을 위해서는 '완전 기초'와 '진짜 기초'가 모두 수행되어야 합니다. 여기서 indegree는 어떤 노드가 수행되기 위해, 선 수행되어야 하는 노드의 수 입니다. 위의 경우 indegree[완전기초] = 0 , indegree[그냥기초] = 1 , indegree[보통] = 2로 표현 가능합니다.  

 

2. q에 초기값 담기

	queue<int> q;
	for (int i = 1; i <= n; i++) {
		if (indegree[i] == 0)
			q.push(i);
	}

그럼 가장 먼저 수행가능 한 노드는 어떤 것 일까요? indegree[] = 0인 값들, 즉 자신이 수행되기 위해 선 수행 할 것이 없는 것들입니다. 그러한 값을 q에 담아줍니다.

 

3. q의 값을 pop하며, 조건에 해당하는 값을 push

while (!q.empty()) {

		int now = q.front();
		q.pop();

		cout << now << " ";

		for (int i = 0; i < v[now].size(); i++) {
			if (--indegree[v[now][i]] == 0) {
				q.push(v[now][i]);
			}
		}
	}

 자 이제 q에서 pop한 값을 now라 한다면, 자신을 수행하기 위해 now가 선 수행되어야하는 노드들의 indegree가 줄어들 것이고, indegree가 0이 된다면 선 수행할 것들이 없다는 의미이므로 q에 담아줍니다.  

소스 코드


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

using namespace std;

vector<int> v[32001];
int indegree[32001];
int n, m;

int main() {

	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
		
	cin >> n >> m;

	for (int i = 0; i < m; i++) {
		int a, b;
		cin >> a >> b;
		v[a].push_back(b);
		indegree[b]++;
	}

	queue<int> q;
	for (int i = 1; i <= n; i++) {
		if (indegree[i] == 0)
			q.push(i);
	}

	while (!q.empty()) {

		int now = q.front();
		q.pop();

		cout << now << " ";

		for (int i = 0; i < v[now].size(); i++) {
			if (--indegree[v[now][i]] == 0) {
				q.push(v[now][i]);
			}
		}
	}
}

개발 환경 : VS2019

질문, 지적 환영합니다!

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

[C++] 백준 1300 k번째 수  (0) 2020.09.07
[C++] 백준 1005 ACM craft  (0) 2020.09.06
[C++] 백준 16637 괄호 추가하기  (0) 2020.09.05
[C++] 백준 1937 욕심쟁이판다  (0) 2020.09.04
[C++] 백준 2193 이친수  (0) 2020.09.04

문제 링크


www.acmicpc.net/problem/16637

 

16637번: 괄호 추가하기

길이가 N인 수식이 있다. 수식은 0보다 크거나 같고, 9보다 작거나 같은 정수와 연산자(+, -, ×)로 이루어져 있다. 연산자 우선순위는 모두 동일하기 때문에, 수식을 계산할 때는 왼쪽에서부터 순�

www.acmicpc.net

접근 방법


 문제를 해결하면서 골치 아팠던 3가지 유의사항이 있었다.

 

※ 유의 사항

1. 입력받는 수의 갯수가 1개일 경우, 해당 값이 정답이 되어야 함

2. 문제에서 요구하는 최댓값이 음수 일 수 있기 때문에, 정답의 초기값을 0으로 설정하지 않는다.

3. 원인은 파악 안했으나, 하단의 소스코드가 문제를 발생시킴.

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

 

 

1. 기본적으로 재귀 함수를 사용하여 괄호 추가가 가능한 모든 경우의 수에 대해 탐색하였고,  연산자와 숫자를 따로 분리하여 관리 하였다.

 

 괄호 추가가 가능한 모든 경우의 수를 탐색하는 코드이다. 

void solve(int now) {

	for (int i = now; i < numbers.size() - 1; i++) {
		if (visited[i] == false) {
			visited[i] = visited[i + 1] = true;
            		ans = max(ans, calc_result());
			solve(i + 1);
			visited[i] = visited[i + 1] = false;	
		}
	}
}

visited[]는 괄호에 감싸져 있는 숫자를 체크하는 배열이다. 문제의 조건에서 괄호안에는 하나의 연산자만 포함한다고 했으므로, 인접한 두 숫자에 대해서 방문체크를 하게 된다. 이후 calc_result()라는 함수안에서 현제 괄호를 포함한 전체 수식을 계산하게 된다.

 

calc_result()

long long calc_result() {

	vector <long long> tmpnumbers;
	vector <char> tmptokens;
	vector <bool> visited_t(tokens.size());
	long long result;
	
    //괄호 값 먼저 계산
	for (int i = 0; i < numbers.size(); i++) {
		if (i < numbers.size() -1 && visited[i] && visited[i + 1]) {
			tmpnumbers.push_back(part_calc(numbers[i], numbers[i + 1], tokens[i]));
			visited_t[i] = true;
			i += 1;
		}
		else 
			tmpnumbers.push_back(numbers[i]);
	}
    
    //괄호에서 사용하고 남은 연산자 배열
	for (int i = 0; i < tokens.size(); i++) 
		if (!visited_t[i]) 
			tmptokens.push_back(tokens[i]);

	//괄호의 계산이 종료 된 이후, 전체 수식 계산 
	result = tmpnumbers[0];
	for (int i = 1; i < tmpnumbers.size(); i++) 
		result = part_calc(result, tmpnumbers[i], tmptokens[i - 1]);

	return result;
}

 

 

 괄호값을 먼저 계산하여 수를 정리하고, 괄호안에서 사용된 연산자를 제외한 연산자를 모아 다시 수식 생성

입력숫자(numbers) -> 정리숫자(tmpnumbers) , 입력연산자(tokens) -> 정리연산자(tmptokens)

 

이 함수에는 3가지 과정이 존재한다. 

ex)  1-(2+3)-(4+5)     

1. 괄호 값 먼저 계산

   앞서 visited[]를 통해 괄호의 위치를 체크하였기 때문에 괄호가 포함된 (a ,(+-*), b)를 part_calc() 함수에서 계산하며,

   괄호안에서 사용된 연산자를 visited_t[]로 사용표시를 한다. 또한 괄호가 정리된 이후 계산 될 새로운 숫자 벡터             tmpnumbers에 push한다. 

   tmpnumbers={1, 5, 9} -> 2와3 사이의 '+'위치 체크, 4와5 사이의 '+'위치 체크, visited_t[1]=visited_t[3]=true

   

2. 괄호에 포함되지 않아 사용되지 않은 연산자를 tmptokens에 담는다.

    tmptokens={-, -}

 

3. 정리된 전체 수식을 계산한다.

 

소스 코드


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

using namespace std;

int n;
vector <long long> numbers;
vector <char> tokens;
bool visited[20];
long long ans = -9999999999;

long long part_calc(long long a, long long b, char token) {

	long long result = 0;
	if (token == '*')
		result = a * b;
	else if (token == '+') 
		result = a + b;
	else if (token == '-') 
		result = a - b;

	return result;
}

long long calc_result() {

	vector <long long> tmpnumbers;
	vector <char> tmptokens;
	vector <bool> visited_t(tokens.size());
	long long result;
	
	for (int i = 0; i < numbers.size(); i++) {
		if (i < numbers.size() -1 && visited[i] && visited[i + 1]) {
			tmpnumbers.push_back(part_calc(numbers[i], numbers[i + 1], tokens[i]));
			visited_t[i] = true;
			i += 1;
		}
		else 
			tmpnumbers.push_back(numbers[i]);
	}
	for (int i = 0; i < tokens.size(); i++) 
		if (!visited_t[i]) 
			tmptokens.push_back(tokens[i]);

	result = tmpnumbers[0];
	for (int i = 1; i < tmpnumbers.size(); i++) 
		result = part_calc(result, tmpnumbers[i], tmptokens[i - 1]);

	return result;
}

void solve(int now) {

	for (int i = now; i < numbers.size() - 1; i++) {
		if (visited[i] == false) {
			visited[i] = visited[i + 1] = true;
			ans = max(ans, calc_result());
			solve(i + 1);
			visited[i] = visited[i + 1] = false;	
		}
	}
	
}

int main() {

	cin >> n;
	for (int i = 0; i <= n/2; i++) {
		int num;
		char token;
		scanf("%1d", &num);
		numbers.push_back(num);
		if (n / 2 != i) {
			scanf("%c", &token);
			tokens.push_back(token);
		}
	}
	solve(0);
	if (n == 1)
		cout << numbers[0];
	else
		cout << ans;
}

개발 환경 : VS2019

질문, 지적 환영합니다!

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

[C++] 백준 1005 ACM craft  (0) 2020.09.06
[C++] 백준 2252 줄 세우기  (0) 2020.09.05
[C++] 백준 1937 욕심쟁이판다  (0) 2020.09.04
[C++] 백준 2193 이친수  (0) 2020.09.04
[C++] 백준 12100 2048(Easy)  (0) 2020.08.10

+ Recent posts