문제 링크


www.acmicpc.net/problem/17136

 

17136번: 색종이 붙이기

<그림 1>과 같이 정사각형 모양을 한 다섯 종류의 색종이가 있다. 색종이의 크기는 1×1, 2×2, 3×3, 4×4, 5×5로 총 다섯 종류가 있으며, 각 종류의 색종이는 5개씩 가지고 있다. <그림 1> 색종이를 크

www.acmicpc.net

 

접근 방법


 DFS로 접근하여 풀 수 있는 문제였으나 시간 초과를 해결하는 과정이 관건이었던 문제였다.

전체 공간인 map[10][10]을 탐색하면서 색종이를 붙여야 되는 공간이 있다면 큰 색종이 부터 시작하여 붙일 수 있는지에 대한 여부를 확인하고 색종이를 붙인다. 완전탐색으로 가장 적은 색종이를 사용하는 경우의 값을 구하면 된다. 

 

 시간초과를 해결하는 방법을 떠올리기가 쉽지가 않았는데 핵심은 현재 공간에 어떤 색종이도 만족하지 않는다면 그 다음 과정에서 이러한 공간을 어차피 만족시킬 수 없다는 것이다. 즉 현재 공간을 만족시킬 수 없다면 다음 과정으로 진행을 시키지 않는 것이 시간초과를 해결하는 핵심이었다.

int solve(int x, int y, int cnt) {

	if (complete()) 
		return cnt;
	if (mincnt <= cnt)
		return mincnt;

	for (int i = x; i < 10; i++) {
		for (int j = 0; j < 10; j++) {
			if (map[i][j] == 1) {
				for (int size = 5; size >= 1; size--) {
					if (inRange(i, j, size)) {
						if (check_map(i, j, size) && cntPerSize[size] > 0) {
							cntPerSize[size]--;
							update_map(i, j, size, 0);							
							mincnt = min(solve(i, j, cnt + 1), mincnt);
							cntPerSize[size]++;
							update_map(i, j, size, 1);
						}
					}
				}
				return mincnt; // !!!중요: 위의 과정에서 만족하지 않는다면 return시킴
			}
		}
	}

	return mincnt;
}

 

소스 코드


#include <iostream>
#include <algorithm>

using namespace std;

int map[10][10];
int cntPerSize[6] = { 0,5,5,5,5,5 };
int mincnt = 99999;

bool inRange(int x, int y, int size) {
	return (x + size - 1 < 10 && y + size - 1 < 10) ? true : false;
}

void update_map(int x, int y, int size, int action) {
	for (int i = x; i < x + size; i++)
		for (int j = y; j < y + size; j++)
			map[i][j] = action;
}

bool check_map(int x, int y, int size) {
	for (int i = x; i < x + size; i++)
		for (int j = y; j < y + size; j++)
			if (map[i][j] == 0)
				return false;
	return true;
}

bool complete() {
	for (int i = 0; i < 10; i++)
		for (int j = 0; j < 10; j++)
			if (map[i][j] == 1)
				return false;
	return true;
}

int solve(int x, int y, int cnt) {

	if (complete()) 
		return cnt;
	if (mincnt <= cnt)
		return mincnt;

	for (int i = x; i < 10; i++) {
		for (int j = 0; j < 10; j++) {
			if (map[i][j] == 1) {
				for (int size = 5; size >= 1; size--) {
					if (inRange(i, j, size)) {
						if (check_map(i, j, size) && cntPerSize[size] > 0) {
							cntPerSize[size]--;
							update_map(i, j, size, 0);							
							mincnt = min(solve(i, j, cnt + 1), mincnt);
							cntPerSize[size]++;
							update_map(i, j, size, 1);
						}
					}
				}
				return mincnt;
			}
		}
	}

	return mincnt;
}

int main() {

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

	for (int i = 0; i < 10; i++)
		for (int j = 0; j < 10; j++) 
			cin >> map[i][j];

	int ans = solve(0,0,0);
	if (ans < 99999)
		cout << ans;
	else
		cout << -1;
}

개발 환경 : VS2017

질문, 지적 환영합니다!

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

[C++] 백준 17406 배열돌리기4  (0) 2021.01.03
[C++] 백준 3190 뱀  (0) 2021.01.03
[C++] 백준 9470 Strahler 순서  (0) 2020.09.25
[C++] 백준 17070 파이프 옮기기 1  (0) 2020.09.18
[C++] 백준 17135 캐슬디펜스  (0) 2020.09.18

문제 링크


www.acmicpc.net/problem/9470

 

9470번: Strahler 순서

지질학에서 하천계는 유향그래프로 나타낼 수 있다. 강은 간선으로 나타내며, 물이 흐르는 방향이 간선의 방향이 된다. 노드는 호수나 샘처럼 강이 시작하는 곳, 강이 합쳐지거나 나누어지는 곳

www.acmicpc.net

접근 방법


 최근 위상 정렬 문제들을 많이 풀면서 대부분 동적계획법의 접근 방법으로 해결이 되어 동적 계획법으로 해결하였다. 

위상을 나타내는 그래프는 vector<int> topology[size]로 나타내었으며, topology[to].push_back(from)으로 입력받아 현제 노드가 어디로부터 왔는지의 정보로 기록하였다.

 

int solve(int now) {

	int &ref = dp[now];
	int number = 0;
	int cnt = 0;

	if (ref != 0) 
		return ref;
	if (topology[now].size() == 0)
		return ref = 1;

	for (int i = 0; i < topology[now].size(); i++) {
		int from = topology[now][i];
		int ret = solve(from);
		if (number < ret) {
			cnt = 1;
			number = ret;
		}
		else if (number == ret) 
			cnt++;
	}

	if (cnt >= 2)
		number += 1;
	return ref = number;
}

 

number로 가장 큰 Strahler순서를 찾았으며, 가장 큰 Strahler순서가 유입된 횟수를 cnt로 카운트하였다.   

 

소스 코드


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

using namespace std;

vector<int> topology[1001];
int dp[1001];
int t, k, m, p;

void init() {
	memset(dp, 0, sizeof(dp));
	for (int i = 0; i <= m; i++) 
		topology[i].clear();
}

int solve(int now) {

	int &ref = dp[now];
	int number = 0;
	int cnt = 0;

	if (ref != 0) 
		return ref;
	if (topology[now].size() == 0)
		return ref = 1;

	for (int i = 0; i < topology[now].size(); i++) {
		int from = topology[now][i];
		int ret = solve(from);
		if (number < ret) {
			cnt = 1;
			number = ret;
		}
		else if (number == ret) 
			cnt++;
	}

	if (cnt >= 2)
		number += 1;
	return ref = number;
}

int main() {

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

	cin >> t;
	while (--t >= 0) {
		cin >> k >> m >> p;
		init();
		for (int i = 0; i < p; i++) {
			int from, to;
			cin >> from >> to;
			topology[to].push_back(from);
		}
		cout << k << " " << solve(m) << "\n";
	}
}

개발 환경 : VS2017

질문, 지적 환영합니다!

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

[C++] 백준 3190 뱀  (0) 2021.01.03
[C++] 백준 17136 색종이 붙이기  (0) 2021.01.02
[C++] 백준 17070 파이프 옮기기 1  (0) 2020.09.18
[C++] 백준 17135 캐슬디펜스  (0) 2020.09.18
[C++] 백준 5670 휴대폰 자판  (0) 2020.09.10

문제 링크


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

+ Recent posts