저자 자체 제작 문제는 저작권을 위해 문제를 작성하지 않았음을 알립니다.
와 진짜 어렵다...카카오 4문제는 어떻게든 풀었지만 30~50분은 커녕 몇 시간씩 걸렸다. 이걸 현장에서 어떻게 풀어...참 갈 길이 너무 멀다.
https://www.acmicpc.net/problem/18406
현재 캐릭터의 점수를 N이라고 할 때 점수 N을 자릿수를 기준으로 반으로 나누어 왼쪽 부분의 각 자릿수의 합과 오른쪽 부분의 각 자릿수의 합을 더한 값이 동일한 상황을 의미한다. 예를 들어 현재 점수가 123,402라면 왼쪽 부분의 각 자릿수의 합은 1+2+3, 오른쪽 부분의 각 자릿수의 합은 4+0+2이므로 두 합이 6으로 동일하여 럭키 스트레이트를 사용할 수 있다.
현재 점수 N이 주어졌을 때, 럭키 스트레이트를 사용할 수 있는 상태인지 아닌지를 알려주는 프로그램을 작성하시오. 럭키 스트레이트를 사용할 수 있다면 "LUCKY"를, 사용할 수 없다면 "READY"라는 단어를 출력한다. 또한 점수 N의 자릿수는 항상 짝수 형태로만 주어진다.
#include <iostream>
#include <vector>
using namespace std;
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
// 과정 1 - 입력을 받고 값을 배열에 저장한다.
string input; cin >> input;
vector<int> score(input.length());
for (int i = 0; i < input.length(); ++i) score[i] = input[i] - '0';
// 과정 2 - 왼쪽 절반 점수 합과 오른쪽 절반 점수 합을 구한다.
int leftVar = 0;
for (int i = 0; i < input.length() / 2; ++i) leftVar += score[i];
int rightVar = 0;
for (int i = input.length() / 2; i < input.length(); ++i) rightVar += score[i];
// 과정 3 - 합한 두 값을 비교하고 결과를 출력한다.
if (leftVar == rightVar) cout << "LUCKY\n";
else cout << "READY\n";
}
알파벳 대문자와 숫자로만 구성된 문자열이 있을 때 모든 알파벳을 오름차순으로 정렬하여 출력한 뒤에 모든 숫자를 더한 값을 이어서 출력한다. 예를 들어, K1KA5CB7 = ABCKK13이다.
4
가 들어있다면, AAAA
를 출력하고, [3]에 2
가 들어있다면, DD
를 출력한다.{1, 0, 0, 2, ... , 0}
이라면, 로 문제에서 원하는 조건을 만족할 수 있다.#include <iostream>
#include <string>
using namespace std;
static int alphabet[23], number[10]; // 외부 해시 배열
int main() {
string input; cin >> input;
// 과정 1: 각 케이스에 알맞게 해시값을 증가시킨다.
for (size_t i = 0; i < input.length(); ++i) {
if (isalpha(input[i])) alphabet[input[i] - 'A']++;
else number[input[i] - '0']++;
}
// 과정 2: 알파벳을 출력한다.
for (int i = 0; i < 23; ++i)
for (int j = 0; j < alphabet[i]; ++j)
cout << static_cast<char>(i + 'A');
// 과정 3: 숫자의 합을 출력한다.
int sum = 0;
for (int i = 0; i < 10; ++i) sum += (i * number[i]);
cout << sum << '\n';
}
https://programmers.co.kr/learn/courses/30/lessons/60057#
데이터 처리 전문가가 되고 싶은 어피치는 문자열을 압축하는 방법에 대해 공부를 하고 있습니다. 최근에 대량의 데이터 처리를 위한 간단한 비손실 압축 방법에 대해 공부를 하고 있는데, 문자열에서 같은 값이 연속해서 나타나는 것을 그 문자의 개수와 반복되는 값으로 표현하여 더 짧은 문자열로 줄여서 표현하는 알고리즘을 공부하고 있습니다.
예를 들어, ababcdcdababcdcd의 경우 문자를 1개 단위로 자르면 전혀 압축되지 않지만, 2개 단위로 잘라서 압축한다면 2ab2cd2ab2cd로 표현할 수 있습니다. 다른 방법으로 8개 단위로 잘라서 압축한다면 2ababcdcd로 표현할 수 있으며, 이때가 가장 짧게 압축하여 표현할 수 있는 방법입니다.
압축할 문자열 s가 매개변수로 주어질 때, 위에 설명한 방법으로 1개 이상 단위로 문자열을 잘라 압축하여 표현한 문자열 중 가장 짧은 것의 길이를 return 하도록 solution 함수를 완성해주세요.[string] [result] "aabbaccc" 7 "ababcdcdababcdcd" 9 "abcabcdede" 8 "abcabcabcabcdededededede" 14 "xababcdcdababcdcd" 17
n
일 때 비교하는 문자의 개수는 2n
개 임을 알아야 한다.2
-> aabbaabb = (aa|bb)(aa|bb)
이렇게 4개씩 잘린다.count
변수를 하나 증가시킨다.2
라면 인덱스도 2칸씩 옮긴다는 뜻)count
변수가 0
이상이라면 압축할 수 있다는 뜻이므로 count
의 자릿수를 파악하고 '정답 문자열 길이'에 추가시킨다.count
가 1~9
라면 1
을 추가, count
가 10~99
라면 2
를 추가해줘야 한다!)#include <string>
using namespace std;
int solution(string s) {
int maxTrial = s.length() / 2; // 최대 가능 분할 회수
int answer = s.length(), tempAnswer = 0;// 분할하지 않은 경우로 초기화
int idx, cnt; // idx는 가리키는 인덱스, cnt는 반복되는 부분문자열 개수
for (int trial = 1; trial <= maxTrial; ++trial) {
idx = 0, tempAnswer = 0, cnt = 0;
while (idx + 2 * trial <= s.length()) { // 압축 가능한 최대 위치까지 진행
while (s.substr(idx, trial) == s.substr(idx + trial, trial)) {
// 압축이 가능하다면 idx 옮기면서 카운팅한다.
idx += trial;
cnt++;
}
// 카운트 변수에 따라 추가해줘야 하는 문자열의 길이가 다르다.
// [주의]: cnt가 1~9 : 길이 1, 10~99: 길이 2, 100~999: 길이 3 임을 고려.
if (cnt > 0) tempAnswer += (trial + to_string(cnt + 1).length());
else tempAnswer += trial;
idx += trial; // idx를 옮겨서 다음 loop로
cnt = 0; // cnt를 초기화한다.
}
tempAnswer += (s.length() - idx); // 남는 문자가 있다면 추가시켜준다.
answer = min(answer, tempAnswer); // 정답을 갱신한다.
}
return answer;
}
https://programmers.co.kr/learn/courses/30/lessons/60059
MxM
으로 주어진key
배열,NxN
으로 주어진Lock
배열이 주어졌을 때,key
배열을 이동하거나 회전시켜서Lock
배열을 완전히 채울 수 있는지 판단하시오.
moving
함수와 오른쪽으로 90도 회전시키는 rotating
함수를 만들어서 풀었다. 그러나 이 문제의 가장 걸림돌이 되는 부분은 바로 인덱스 범위 문제였다. 본 풀이법으로는 이 문제를 해결하기 까다로웠으며 풀이과정이 복잡해서 최적화가 필요하다고 판단했다.rotating
함수는 놔두고 moving
함수는 제거하자. moving
함수를 사용하지 않고도 key
배열을 움직일 수 있는 방법을 떠올리는 것이 이 문제의 핵심이다.Lock
배열의 가로 3배, 세로 3배만큼의 2차원 배열을 만들고 Lock
배열을 가운데에 넣는다.Lock
배열의 가장 좌측, 가장 위칸에서 부터 시작해서Key
배열을 이동시키면서 Lock
배열과 겹치지는 않는지(비정상, false), 빈 칸은 없는지(비정상, false) 혹은 모든 칸을 잘 채우는지(정상, true) 확인하는 방법을 사용한다.key
배열은 S
포인트부터 시작해서 가로로 E
포인트, 세로로 E
포인트까지 이동시키면서 Lock
배열을 모두 채우는지 확인한다.#include <vector>
using namespace std;
static int lockSize, keySize;
// 배열을 오른쪽으로 90도 회전시키는 코드.
void rotating (vector<vector<int>>& vec) {
vector<vector<int>> temp = vec;
for (int y = 0; y < keySize; ++y)
for (int x = 0; x < keySize; ++x)
temp[y][x] = vec[keySize - 1 - x][y];
vec = temp;
}
bool checkValid(vector<vector<int>> map, vector<vector<int>> key, int y, int x) {
// key의 시점부터 시작해서 값을 증가시킨다.
for(int i = y; i < y + keySize; ++i)
for (int j = x; j < x + keySize; ++j)
map[i][j] += key[i - y][j - x];
// 겹치는 칸은 2가 되고, 비어있는 칸은 0이 된다. 발견 시 false 반환한다.
for (int i = lockSize; i < 2 * lockSize; ++i)
for (int j = lockSize; j < 2 * lockSize; ++j)
if (map[i][j] == 2 || map[i][j] == 0) return false;
return true;
}
bool solution(vector<vector<int>> key, vector<vector<int>> lock) {
lockSize = lock.size(), keySize = key.size();
// lock의 가로, 세로가 3배만큼 큰 배열 생성 후 lock을 가운데에 넣는다.
vector<vector<int>> map(3 * lockSize, vector<int>(3 * lockSize, 0));
for (int y = 0; y < lockSize; ++y)
for (int x = 0; x < lockSize; ++x)
map[lockSize + y][lockSize + x] = lock[y][x];
// key의 시점, 종점을 계산한다. (그림을 그려서 확인하면 이해 쉬움)
int newStartPos = lockSize - (keySize - 1);
int newEndPos = 2 * lockSize - 1;
// key를 한 칸씩 이동시키면서 4번 회전시키고 열쇠가 들어가는지 확인한다.
for (int y = newStartPos; y <= newEndPos; ++y)
for (int x = newStartPos; x <= newEndPos; ++x)
for (int cnt = 0; cnt < 4; ++cnt)
if(checkValid(map, rotating(key), y, x)) return true;
return false;
}
https://www.acmicpc.net/problem/3190
'Dummy' 라는 도스게임이 있다. 이 게임에는 뱀이 나와서 기어다니는데, 사과를 먹으면 뱀 길이가 늘어난다. 뱀이 이리저리 기어다니다가 벽 또는 자기자신의 몸과 부딪히면 게임이 끝난다. 게임은 NxN 정사각 보드위에서 진행되고, 몇몇 칸에는 사과가 놓여져 있다. 보드의 상하좌우 끝에 벽이 있다. 게임이 시작할때 뱀은 맨위 맨좌측에 위치하고 뱀의 길이는 1 이다. 뱀은 처음에 오른쪽을 향한다. 뱀은 매 초마다 이동을 하는데 다음과 같은 규칙을 따른다.
먼저 뱀은 몸길이를 늘려 머리를 다음칸에 위치시킨다.
만약 이동한 칸에 사과가 있다면, 그 칸에 있던 사과가 없어지고 꼬리는 움직이지 않는다.
만약 이동한 칸에 사과가 없다면, 몸길이를 줄여서 꼬리가 위치한 칸을 비워준다. 즉, 몸길이는 변하지 않는다. 사과의 위치와 뱀의 이동경로가 주어질 때 이 게임이 몇 초에 끝나는지 계산하라.
1
이다.L
, D
명령도 다른 방향으로 전환된다는 점을 고려하자.queue
에 저장해둔 뒤 정해진 시간이 되면 명령을 수행하고 pop()
해서 다음 명령까지 기다린다.2
로 표시하고, 뱀은 1
로 표시하고, 빈칸은 0
으로 표시한다.1
로 만들고, 꼬리가 있던 자리를 0
으로 만드는 것은 옳은 방법이 아니다. 왜냐하면 단 한 번이라도 방향전환이 이뤄졌을 경우 꼬리가 그 방향전환을 따라갈 방법이 없기 때문이다.pair<int, int>
구조로 어딘가에 저장한 뒤 매 초마다 모든 요소의 .first
를 하나씩 차감한 뒤 0
이 되면 꼬리도 방향을 전환하도록 하면 되긴하다.deque
자료구조를 이용해서 머리는 push_front()
하며 전진하고, 꼬리는 pop_back()
해주는 식으로 꼬리를 바꿔나가는 방법을 사용한다.front()
를 확인해서 수행할 시간인지 아닌지 판단한다.pop()
해서 제거한다. (FIFO)0
, 빈칸으로 바꿔주고 pop_back()
한다.1
로 만들어주고 push_front()
해서 새로운 머리로 만들어준다.#include <iostream>
#include <queue>
#include <deque>
using namespace std;
static int N, K, L, board[102][102];
void changeDir(int& offsetY, int& offsetX, char c) {// 90도 회전
if (offsetY == 0) {
if (offsetX == 1) offsetY = (c == 'L') ? -1 : 1;// 오른쪽->위쪽
else offsetY = (c == 'L') ? 1 : -1; // 왼쪽->아래쪽
offsetX = 0;
} else {
if (offsetY == 1) offsetX = (c == 'L') ? 1 : -1;// 아래쪽->오른쪽
else offsetX = (c == 'L') ? -1 : 1; // 위쪽->왼쪽
offsetY = 0;
}
}
int solve(queue<pair<int, char>> command) {
int sec = 0, dirY = 0, dirX = 1; // 초기 시간: 0, 최초 방향: 오른쪽
deque<pair<int, int>> snake;
snake.push_back({1, 1}); // 초기 Head = Tail = (1, 1)에 위치함.
board[snake.front().first][snake.front().second] = 1; // 뱀의 위치 = 1
while (true) {
int headY = snake.front().first, headX = snake.front().second;
// [기저조건]: 범위를 벗어나는 경우 게임이 종료된다.
if (headY < 1 || headY > N || headX < 1 || headX > N) break;
// 정해진 시간에 명령을 수행한다. 수행한 명령은 제거한다.
if (command.front().first == sec) {
changeDir(dirY, dirX, command.front().second);
command.pop();
}
sec++;
headY += dirY; headX += dirX; // 정해진 방향대로 머리가 먼저 움직인다.
if (board[headY][headX] == 1) break; // 만일 뱀의 몸통과 닿으면 게임 종료.
if (board[headY][headX] == 0) { // 빈칸인 경우 tail을 0으로 바꿔준다.
board[snake.back().first][snake.back().second] = 0;
snake.pop_back(); // tail을 다음 칸으로 갱신해준다.
}
board[headY][headX] = 1; // 머리가 늘어난 부분을 1로 만들어주고,
snake.push_front({headY, headX}); // 새로운 head를 snake 배열 맨 앞에 추가해준다.
}
return sec;
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> N >> K;
for (int i = 0; i < K; ++i) {
int y, x; cin >> y >> x;
board[y][x] = 2; // 빈칸: 0, 뱀: 1, 사과: 2 표시.
}
cin >> L;
queue<pair<int, char>> command;
for (int i = 0; i < L; ++i) {
int s; char c; cin >> s >> c;
command.push(make_pair(s, c));
}
cout << solve(command) << '\n';
}
102x102
칸으로 만든 이유?https://programmers.co.kr/learn/courses/30/lessons/60061
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
typedef struct Component {
int x, y, thing, op;
Component(const vector<int>& vec) {
if (vec.size() == 3) x = vec[0], y = vec[1], thing = vec[2];
else x = vec[0], y = vec[1], thing = vec[2], op = vec[3];
}
}Component;
// 현재 answer 구조가 가능한지 불가능한지 판단한다.
bool isPossible(const vector<vector<int>>& answer) {
for (int i = 0; i < answer.size(); ++i) {
Component comp(answer[i]);
bool check = false;
if (!comp.thing) { // [1]: '기둥'을 검사한다.
if (comp.y == 0) check = true; // [기저]: 기둥이 바닥에 있는 경우 = 정상
for (int j = 0; j < answer.size(); ++j) {
Component temp(answer[j]);
// 기둥이 보 위에 있는 경우, 다른 기둥 위에 있는 경우 = 정상
if (comp.x - 1 == temp.x && comp.y == temp.y && temp.thing) check = true;
else if (comp.x == temp.x && comp.y == temp.y && temp.thing) check = true;
else if (comp.x == temp.x && comp.y - 1 == temp.y && !temp.thing) check = true;
}
if (!check) return false;
} else { // [2]: '보'를 검사한다.
bool leftCheck = false, rightCheck = false;
for (int j = 0; j < answer.size(); ++j) {
Component temp(answer[j]);
// 보의 한 쪽 끝이 기둥 위에 있는 경우 = 정상
if (comp.x == temp.x && comp.y - 1 == temp.y && !temp.thing) check = true;
else if (comp.x + 1 == temp.x && comp.y - 1 == temp.y && !temp.thing) check = true;
// 양쪽이 다른 보에 연결되어 있는 경우 = 정상
if (comp.x - 1 == temp.x && comp.y == temp.y && temp.thing) leftCheck = true;
if (comp.x + 1 == temp.x && comp.y == temp.y && temp.thing) rightCheck = true;
if (leftCheck && rightCheck) check = true;
}
if (!check) return false;
}
}
return true;
}
vector<vector<int>> solution(int n, vector<vector<int>> build_frame) {
vector<vector<int>> answer;
for (int i = 0; i < build_frame.size(); ++i) {
Component comp(build_frame[i]);
if (comp.op) { // [1]: 설치하는 경우
vector<int> temp {comp.x, comp.y, comp.thing};
answer.push_back(temp); // answer 배열에 추가(설치)하고 가능한지 여부 판단한다.
if (!isPossible(answer)) answer.pop_back(); // 불가능 하다면 다시 뺀다.
} else { // [2]: 해체하는 경우
int idx = 0; // 해체하려는 물건(기둥 또는 보)이 answer의 몇 번째 인덱스에 있는지 우선 찾는다.
for (int j = 0; j < answer.size(); ++j)
if (comp.x == answer[j][0] && comp.y == answer[j][1] && comp.thing == answer[j][2]) idx = j;
vector<int> temp = answer[idx];
answer.erase(answer.begin() + idx); // 해체할 물건을 찾았으니 answer에서 지우고, 가능한지 여부 판단한다.
if (!isPossible(answer)) answer.push_back(temp); // 불가능하다면 다시 넣는다.
}
}
sort(answer.begin(), answer.end()); // 오름차순으로 정렬 후 반환한다.
return answer;
}
https://www.acmicpc.net/problem/15686
크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸, 왼쪽에서부터 c번째 칸을 의미한다. r과 c는 1부터 시작한다.
이 도시에 사는 사람들은 치킨을 매우 좋아한다. 따라서, 사람들은 "치킨 거리"라는 말을 주로 사용한다. 치킨 거리는 집과 가장 가까운 치킨집 사이의 거리이다. 즉, 치킨 거리는 집을 기준으로 정해지며, 각각의 집은 치킨 거리를 가지고 있다. 도시의 치킨 거리는 모든 집의 치킨 거리의 합이다. 임의의 두 칸 (r1, c1)과 (r2, c2) 사이의 거리는 |r1-r2| + |c1-c2|로 구한다.
N
은 50까지, M
은 13까지이므로 충분히 모든 경우를 탐색할 수 있다.M
개 고를 때 최소 거리 합을 구하면 된다.{1, 2, 1, 1}
이므로 합이 5
가 나온다.#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace std;
static int N, M;
static vector<pair<int, int>> home, chicken;
inline int getDistance(pair<int, int> lhs, pair<int, int> rhs) {
return abs(lhs.first - rhs.first) + abs(lhs.second - rhs.second);
}
int solve() {
// 임의의 i번째 집으로부터 모든 치킨집까지의 거리를 구하고 i행에 저장한다.
vector<vector<int>> memoSum(home.size(), vector<int>(chicken.size(), 0));
for (int i = 0; i < home.size(); ++i)
for (int j = 0; j < chicken.size(); ++j)
memoSum[i][j] = getDistance(home[i], chicken[j]);
// 모든 치킨집에서 M개 조합을 고르기 위해 마스킹을 사용할 것이다.
vector<bool> mask(chicken.size(), true);
for (int i = 0; i < M; ++i) mask[i] = false;
int ret = INT16_MAX; // 절대 나올 수 없는 큰 값으로 초기화한다.
vector<int> tempSum(home.size(), 0); // 각 집의 최소 '치킨거리'값 저장
do {
for (int i = 0; i < home.size(); ++i) {
int temp = INT16_MAX;
for (int j = 0; j < chicken.size(); ++j)
if (mask[j] == 0) temp = min(temp, memoSum[i][j]);
tempSum[i] = temp;
}
ret = min(ret, accumulate(begin(tempSum), end(tempSum), 0));
tempSum.assgin(home.size(), 0) // 다음 조합을 위해 초기화.
} while (next_permutation(begin(mask), end(mask))); // 다음 조합을 구한다.
return ret;
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
cin >> N >> M;
for (int y = 1; y <= N; ++y)
for (int x = 1; x <= N; ++x) {
int input; cin >> input;
if (input == 1) home.push_back({y, x});
else if (input == 2) chicken.push_back({y, x});
}
cout << solve() << '\n';
}
https://programmers.co.kr/learn/courses/30/lessons/60062
레스토랑의 구조는 완전히 동그란 모양이고 외벽의 총 둘레는 n미터이며, 외벽의 몇몇 지점은 추위가 심할 경우 손상될 수도 있는 취약한 지점들이 있습니다. 따라서 내부 공사 도중에도 외벽의 취약 지점들이 손상되지 않았는 지, 주기적으로 친구들을 보내서 점검을 하기로 했습니다. 다만, 빠른 공사 진행을 위해 점검 시간을 1시간으로 제한했습니다. 친구들이 1시간 동안 이동할 수 있는 거리는 제각각이기 때문에, 최소한의 친구들을 투입해 취약 지점을 점검하고 나머지 친구들은 내부 공사를 돕도록 하려고 합니다. 편의 상 레스토랑의 정북 방향 지점을 0으로 나타내며, 취약 지점의 위치는 정북 방향 지점으로부터 시계 방향으로 떨어진 거리로 나타냅니다. 또, 친구들은 출발 지점부터 시계, 혹은 반시계 방향으로 외벽을 따라서만 이동합니다.
외벽의 길이 n, 취약 지점의 위치가 담긴 배열 weak, 각 친구가 1시간 동안 이동할 수 있는 거리가 담긴 배열 dist가 매개변수로 주어질 때, 취약 지점을 점검하기 위해 보내야 하는 친구 수의 최소값을 return 하도록 solution 함수를 완성해주세요.
#include <vector>
#include <algorithm>
using namespace std;
int solution(int n, vector<int> weak, vector<int> dist) {
int cntWeakSpot = weak.size();
for (int i = 0; i < cntWeakSpot; ++i)
weak.push_back(weak[i] + n);
int answer = dist.size() + 1; // 초기값: (최대 친구 수 + 1)
for (int i = 0; i < cntWeakSpot; ++i) { // 시작 위치를 변경해주며 탐색한다.
do {
int cntFriend = 1; // 첫 번째 친구가 i번째 시작 위치에서 이동한다.
int pos = weak[i] + dist[cntFriend - 1];
for (int j = i + 1; j < cntWeakSpot + i; ++j) {
if (pos < weak[j]) { // 이전 사람이 j번째 약점 검사 못했으므로 친구 더 필요함.
cntFriend++; // 필요한 친구 수 추가해준다.
if (cntFriend > dist.size()) break; // 더 부를 친구 없다면 반복문 탈출.
pos = weak[j] + dist[cntFriend - 1]; // 방금 부른 친구가 j번째 시작 위치에서 이동한다.
}
}
answer = min(answer, cntFriend); // 최소 친구 수 갱신한다.
} while (next_permutation(begin(dist), end(dist))); // 모든 친구 조합에 대해서 테스트 하기 위해 순열 사용.
}
if (answer > dist.size()) return -1; // [예외]: 현재 인원으로 약점을 모두 검사할 수 없는 경우.
return answer;
}
구현 문제 모음 잘봤습니다! 감사합니다!