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

 

11729번: 하노이 탑 이동 순서

세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로 옮기려 한다. 한 번에 한 개의 원판만을 다른 탑으로 옮길 수 있다. 쌓아 놓은 원판은 항상 위의 것이 아래의 것보다 작아야 한다. 이 작업을 수행하는데 필요한 이동 순서를 출력하는 프로그램을 작성하라. 단, 이동 횟수는 최소가 되어야 한다. 아래 그림은 원판이 5

www.acmicpc.net

 

중학교 수학 문제에 등장했던 하노이의 탑이다.

 

재귀를 이용한다. 5개의 링을 옮기는 과정 속에는 4개의 링을 옮기는 과정이 포함된다.

 

4개의 링을 옮기는 과정 속에는 3개의 링을 옮기는 과정이 포함된다.

 

이런식으로 끄적이며 생각해보면 재귀라는 아이디어를 얻을 수 있을 것이다.

 

예를 들어서, 첫번째 자리에 있던 모든 링들을 세번째 자리로 옮긴다고 하자.

 

이를 위해서는 가장 아래에 깔려있는 가장 큰 링을 빼내야 한다. 이를 위해서는 그것 위에 있는 n - 1 개의 링을 모두 임시적으로 다른 곳에 옮길 필요가 있다.

 

주의할 것은, 2의 20승을 계산할 때, pow함수의 출력이 의도한대로 나오지 않는다는 것이다. 이를 위해서 1 << n 연산을 이용한다.

 

#include<iostream>
#include<math.h>
using namespace std;

void hanoi(int src, int dst, int n) {
	if (n == 1) {
		cout << src << ' ' << dst << '\n';
		return;
	}
	int temp = 6 - src - dst;
	hanoi(src, temp, n - 1); //1 ~ n-1 링을 중간지점에 옮긴다
	cout << src << ' ' << dst << '\n'; //가장 커다란 링을 목적지로 옮긴다.
	hanoi(temp, dst, n - 1); //중간지점에 있던 n - 1 개의 링들을 목적지로 옮긴다.
}

int main(void) {
	int n;
	cin >> n; //옮기는 링의 개수
	//cout << pow(2, n) - 1 << '\n';// 이렇게 쓰면 틀림 n = 20일때 output이 이상해짐
	cout << (1 << n) - 1 << '\n'; // 2의 n승 -1
	hanoi(1, 3, n);

	return 0;
}

 

재귀로 풀이를 진행할 때는

 

1. base condition을 명확히 정의한다.

 

2. 재귀 식을 찾는다.

n개를 옮기려면, n - 1을 다른곳으로 옮긴 이후에 하나 옮기고 다시 n - 1을 목적지로 옮겨야해.. 와 같은

 

이 때, 이전 과정이 올바르게 작동할 것이라는 가정을 하고, 현재의 함수가 올바르게 결과를 내놓을지 판단한다.

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

 

1629번: 곱셈

첫째 줄에 A, B, C가 빈 칸을 사이에 두고 순서대로 주어진다. A, B, C는 모두 2,147,483,647 이하의 자연수이다.

www.acmicpc.net

 

A^B mod m을 연산하는 프로그램을 작성하는 문제이다.

 

수의 범위가 20억까지 가능하기 때문에 long long 타입을 활용한 것 이외에 재귀를 진행할 때도 아이디어가 하나 필요했다.

 

B가 홀수냐 짝수냐에 따라서 경우를 나눠줬다.

 

재귀의 base condition은 B가 0일 때 1을 반환하는 것이 된다.

 

또한 overflow를 방지하기 위해 계산하는 중간중간에 modular 연산을 수행해줄 필요가 있다.

 

XYmodM = (XmodM * YmodM) mod M 인 것을 이용하면 되겠다.

 

지수가 홀수인 경우, 계산 이후에 a를 한 번 더 곱해줘야 하고, 이 과정에서 modular 연산을 한번 더 수행해줘야 한다.

 

#include<iostream>
using namespace std;
typedef long long ll;

ll POW(ll a, ll b, ll m){
    if(b == 0) return 1;
    ll val = POW(a, b/2, m);
    val = val * val % m;
    
    //b가 짝수, 홀수인 경우 나눠서 처리
    if(b % 2 == 0) return val;
    else
        return (val * a) % m;
    
}

int main(void){
    int a, b, m;
    cin >> a >> b >> m;
    cout << POW(a, b, m);
    return 0;
}

문제 링크: https://programmers.co.kr/learn/courses/30/lessons/42840

 

알고리즘 연습 - 모의고사 | 프로그래머스

수포자는 수학을 포기한 사람의 준말입니다. 수포자 삼인방은 모의고사에 수학 문제를 전부 찍으려 합니다. 수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다. 1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ... 2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ... 3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3,

programmers.co.kr

 

수포자가 수학을 찍는 규칙을 확인해서, 등수를 가려내는 문제이다.

 

코드를 좀 더 깔끔하게 구현할 수 있을텐데 직관적으로 빠르게 구현하고자 했기 때문에 코드가 다소 난잡하다.

 

먼저 세 학생의 답안 마킹 방법을 배열로 설정해뒀다.

 

이어서 맞은 개수와, 학생 index pair를 벡터에 넣고 정렬을 수행했다.

 

정렬 조건을 만들어주는 bool 함수만 조심해서 구현하고, 문제 조건에 맞게 조건문을 작성해준다.

 

#include <string>
#include <vector>
#include<bits/stdc++.h>

using namespace std;
int fir[10000];
int sec[10000];
int thr[10000];
bool cmp(pair<int, int> a, pair<int, int> b){
    //T 면 a가 앞
    if(a.second > b.second)
        return true;
    else 
        return false;
}
vector<int> solution(vector<int> answers) {
    vector<int> answer;
    for(int i = 0 ; i < 10000 ; i ++){
        if(i % 5 == 0) fir[i] = 1;
        else if(i % 5 == 1) fir[i] = 2;
        else if(i % 5 == 2) fir[i] = 3;
        else if(i % 5 == 3) fir[i] = 4;
        else if(i % 5 == 4) fir[i] = 5;
    }
    for(int i = 0 ; i < 10000 ; i ++){
        if(i % 2 == 0) sec[i] = 2;
        else if(i % 8 == 1) sec[i] = 1;
        else if(i % 8 == 3) sec[i] = 3;
        else if(i % 8 == 5) sec[i] = 4;
        else if(i % 8 == 7) sec[i] = 5;
    }
    for(int i = 0 ; i < 10000 ; i++){
        if(i % 10 == 0 || i % 10 == 1) thr[i] = 3;
        else if(i % 10 == 2 || i % 10 == 3) thr[i] = 1;
        else if(i % 10 == 4 || i % 10 == 5) thr[i] = 2;
        else if(i % 10 == 6 || i % 10 == 7) thr[i] =4;
        else if(i % 10 == 8 || i % 10 == 9) thr[i] =5;
    }
    vector<pair<int, int> > v{ {1,0}, {2,0}, {3,0} } ;
    for(int i = 0 ; i < answers.size() ; i++){
        if(fir[i] == answers[i]) v[0].second++;
        if(sec[i] == answers[i]) v[1].second++;
        if(thr[i] == answers[i]) v[2].second++;
    }
    sort(v.begin(), v.end(), cmp);
    
    if(v[0].second != 0){
       if(v[0].second != v[1].second)
           answer.push_back(v[0].first);
        else{
            if(v[1].second == v[2].second){
                answer.push_back(v[0].first);
                answer.push_back(v[1].first);
                answer.push_back(v[2].first);
                }
            else{
                answer.push_back(v[0].first);
                answer.push_back(v[1].first);
            }
        }
    }
    sort(answer.begin(), answer.end());
    return answer;
}

문제 링크: https://programmers.co.kr/learn/courses/30/lessons/42576

 

알고리즘 연습 - 완주하지 못한 선수 | 프로그래머스

수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다. 마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요. 제한사항 마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다. completion의 길이는 partic

programmers.co.kr

 

해쉬 카테고리에 있어서 무조건 그렇게 풀려고 시도했다.

 

결론적으로 multimap을 이용해서, 선수들의 이름을 map의 key에 넣고,

 

value는 이 문제에선 큰 의미가 없기 때문에 그냥 순서로 정해주었다.

 

이후, 완주 한 사람을 찾아서 원래의 map에서 지워주면 자연스럽게 남는 사람이 완주하지 못한 사람이 된다.

 

#include<iostream>
#include<map>
#include<string>
#include<vector>
using namespace std;
string solution(vector<string> participant, vector<string> completion) {
	string answer = "";

	multimap<string, int> mm;
	for (int i = 1; i <= participant.size(); i++) {
		mm.insert({ participant[i - 1], i });
	}
	for (int i = 0; i < completion.size(); i++) {
		mm.erase(mm.lower_bound(completion[i]));
	}
	return mm.begin()->first;
}

 

STL의 사용이 익숙하지 않았기 때문에, 사전에 공부가 필요했다.

 

동명이인이 있을 수 있다고 문제 조건에 명시되어 있으므로 map이 아닌 multimap을 사용했다.

 

pair를 다룬다고 생각하고 {key, value} 꼴로 map에 insert 해주면 된다.

 

lower_bound는 같은 key를 가진 데이터들 중에서, 가장 첫번째의 원소를 의미하는 것이다.

 

upper_bound를 이 문제에서 사용하지는 않았지만, upper_bound를 사용하면, 가장 마지막 원소가 아니라,

 

가장 마지막 원소보다 한 칸 더 이후를 의미하는 것 같았다.

 

자료구조에 대해 곧 자세히 정리해야겠다.

 

 

문제 링크: https://programmers.co.kr/learn/courses/30/lessons/42746

 

알고리즘 연습 - 가장 큰 수 | 프로그래머스

0 또는 양의 정수가 주어졌을 때, 정수를 이어 붙여 만들 수 있는 가장 큰 수를 알아내 주세요. 예를 들어, 주어진 정수가 [6, 10, 2]라면 [6102, 6210, 1062, 1026, 2610, 2106]를 만들 수 있고, 이중 가장 큰 수는 6210입니다. 0 또는 양의 정수가 담긴 배열 numbers가 매개변수로 주어질 때, 순서를 재배치하여 만들 수 있는 가장 큰 수를 문자열로 바꾸어 return 하도록 solution 함수를 작성해주세요.

programmers.co.kr

0 또는 양의 정수를 가지는 배열이 주어질 때, 그것을 나열해서 만들 수 있는 가장 큰 수를 string type으로 반환하는 문제이다.

 

처음에는 단순히 사전식 정렬 이후에, 가장 큰 자릿수의 숫자가 같다면, 길이가 짧은 수가 앞에 와야 큰 수를 만들어낸다고 생각했다.

 

너무 단순한 생각이었다. 비교하고자 하는 숫자의 길이가 다를 경우에 여러 가지 경우를 고려해줘야 한다.

 

그렇다면 비교하고자 하는 숫자의 길이를 같게 만들어주면 된다.

 

45와 4642 중에 어떤 수를 앞에 두어야 올바른 정렬일까 생각해보면,

 

두 수를 두 가지 방법으로 붙여서 454642와 464245를 비교하면 되는 것이다.

 

 

#include <string>
#include <vector>
#include<bits/stdc++.h>

using namespace std;
bool cmp(string a, string b){
    //a가 b보다 먼저 나오는 경우가 true
    if(a + b > b + a)
        return true;
    else
        return false;
}
string solution(vector<int> numbers) {
    string answer = "";
    
    //길이가 다른 경우가 문제니까 길이를 같게 해주려면?
    //비교하는 수 두개를 순서를 바꿔가면서 이어서 붙여보면됨.
    //길이만 같으면 사전순 비교나 크기 비교나 똑같으니까
    
    vector<string> v;
    for(int i = 0 ; i < numbers.size() ; i++)
        v.push_back(to_string(numbers[i]));    
    sort(v.begin(), v.end(), cmp);
    
    //전부 0일 수도 있나?
    bool allZero = true;
    
    for(int i = 0 ; i < v.size() ; i++){
        answer += v[i];
        if(v[i] != "0") allZero = false;
    }
    if(allZero)
        return "0";
    else
        return answer;
    
}

 

조합과 관련된 문제라고 기억한다. 조합과 백트레킹을 매끄럽게 다룰 수 있었다면 조금 더 빠르게 풀고 넘어갈 수 있었던 문제였다. 역량테스트 A형스러운 문제였던 기억이 난다.

 

가장 기본적으로 소수를 판별할 수 있어야 한다.

 

다음으로 정수의 자리수를 없애는 방식으로 수의 변화가 일어난다. 따라서 문자열 처리를 해주면 되겠다.

 

정수의 자리수에서 하나의 숫자를 지우는 것을 조합의 개념으로 이해하고, 문제를 풀이했다.

 

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
bool isPrime(int n) {
	if (n == 1) return false;
	for (int i = 2; i * i <= n; i++)
		if (n % i == 0) return false;
	return true;
}
int Max = -1;
int func(int n, int res) {
	
	string str = to_string(n);
	
	if (str.length() == 1) {
		if (isPrime(n)) {
			res++;
			if (Max < res) Max = res;
			//printf("\n %d 일 때 res는 %d\n", n, res);
			return res;
		}
		else {
			if (Max < res) Max = res;
			return res;
		}
	}
	
	if (isPrime(n)) {
		res++;
		if (Max < res) Max = res;
		//printf("\n %d 일 때 res는 %d\n", n, res);
	}
	else {
		//cout << n << "은 소수가 아님\n";
		if (Max < res) Max = res;
		return res;
	}
	
	//vector<int> a; //몇개의 수에서
	int a[6];
	
	for (int i = 0; i < str.length(); i++) {
		a[i] = stoi(str.substr(i, 1));
	}
	
	int select[7] = { 0,1,1,1,1,1,1 };
	
	do {
		string temp;
		for (int i = 0; i < str.length(); i++) {
		
			if (select[i])
				temp = temp + to_string(a[i]);
		}
		//cout << temp << ' ' << res << '\n';
		func(stoi(temp.substr(0,temp.length())),res);
	}while (next_permutation(select, select + str.length()));

}
int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int tc;
	cin >> tc;
	for (int T = 1; T <= tc; T++) {
		cout << "Case #" << T << '\n';
		Max = -1;
		int a, b;
		cin >> a >> b;
		func(a, 0);
		int MaxA = Max;
		Max = -1;
		func(b, 0);
		int MaxB = Max;

		//printf("Amax = %d Bmax = %d\n", MaxA, MaxB);
		if (MaxA > MaxB)
			cout << 1 << '\n';
		else if (MaxA < MaxB)
			cout << 2 << '\n';
		else
			cout << 3 << '\n';
	}
	return 0;
}
#include<iostream>
#include<vector>
using namespace std;
int mem[100001];
int solution(vector<int> prices)
{
	int min = 1000001;
	for (int i = 0; i < prices.size(); i++) {
		if (prices[i] < min) {
			min = prices[i];
		}
		mem[i] = min;
	}
	int max = 0;
	for (int i = 0; i < prices.size(); i++) {
		if (prices[i] - mem[i] > max) {
			max = prices[i] - mem[i];
		}
	}
	return max;
}

 

prices 배열의 어떤 index까지의 최솟값을 mem 배열에 저장한다.

 

가령 prices = {1,5,2,6,2,4,0} 이런 상황이라면 mem = {1,1,1,1,1,1,0} 이렇게 되는 것이다.

 

최소의 가격일 때 구매해서 최대의 가격에 되팔아서 이윤의 최대치를 구하는 문제에 이용할 수 있다.

 

index가 시간을 의미한다고 볼 수 있다. 같은 index에서 prices와 mem의 차이가 가장 큰 지점이 정답이 된다.

 

최대 마진을 내야 하는 경우로 기억

'알고리즘 문제 풀이 > 문제 접근 방법' 카테고리의 다른 글

다익스트라 - 함께여행  (0) 2022.11.18
시간 줄이기  (0) 2019.08.20
메모리초과  (0) 2019.08.16
런타임 에러 발생 이유 (추가중)  (0) 2019.07.25
투포인터 응용  (0) 2019.07.09

가령 상황을 하나 혹은 두 개의 배열로 나타낼 수 있다고 하자.

 

배열이 하나라면, index 0부터 최댓값을 담는 배열을 만들고,

 

가장 끝 index부터 최댓값을 담는 배열을 만들고, 상황에 맞게 사용하는 것이다.

 

꼭 최댓값이 아니더라도, 한쪽에서는 최대, 다른 한쪽에서는 최소를 잡을 수도 있겠다.

 

 

이런 유사한 방식을 세번 이상 사용한 것 같다. 아마 다른 사람이 보면 이게 무슨 말인가 싶겠지만,

 

일단 내가 기억하고자 하는 목적으로 정리를 해두겠다.

 

#define ll long long
#include<iostream>
#include<vector>
using namespace std;

ll lmax[300001], rmin[300001];
int solution(vector<int> &T) {
	ll Max = T[0];
	for (int i = 0; i < T.size(); i++) {
		if (T[i] > Max)
			Max = T[i];
		lmax[i] = Max;
	}

	ll Min = T.at(T.size() - 1);
	for (int i = T.size() - 1; i >= 0; i--) {
		if (T[i] < Min)
			Min = T[i];
		rmin[i] = Min;
	}

	for (int i = 0; i < T.size() - 1; i++) {
		if (lmax[i] < rmin[i + 1]) {
			//len = i + 1;
			return i + 1;
		}
	}
}
int main(void) {
	vector<int> vec{ -5, -5, -5, -42, 6, 12 };
	solution(vec);
	return 0;
}

 

주어진 배열에서, 조건에 따라 부분 배열을 찾는 문제이다. vec 벡터를 두 개의 벡터로 나눌 것인데, 하나의 벡터의 모든 성분이 또 다른 벡터의 어떤 성분보다도 작도록 벡터를 양분하려고 할 때, 위와 같은 방법을 사용할 수 있다.

 

 

 

 

 

'알고리즘 문제 풀이 > 문제 접근 방법' 카테고리의 다른 글

다익스트라 - 함께여행  (0) 2022.11.18
시간 줄이기  (0) 2019.08.20
메모리초과  (0) 2019.08.16
런타임 에러 발생 이유 (추가중)  (0) 2019.07.25
배열 최대/최소 기억  (0) 2019.07.09

dp문제였다. 자세한 복기는 문제가 업로드된 이후에 해보겠다.

 

#include <iostream>

using namespace std;
int d[1000001];
int Answer;

int main(int argc, char** argv)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	//setbuf(stdout, NULL); //주석풀고제출

	int T, test_case;
	
	d[2] = 1;

	int i = 4;
	while (i <= 1000000) {
		if (i % 2 == 0) {
			d[i] = d[i / 2] + 1;
			i--;
			continue;
		}
		else {
			d[i] = d[i + 1] + 1;
			i += 3;
			continue;
		}
	}

	cin >> T;
	
	for (test_case = 0; test_case < T; test_case++)
	{
		int n1, n2;
		cin >> n1 >> n2;
		Answer = 0;
		
		for (int i = n1; i <= n2; i++) {
			Answer = Answer + d[i];
		}
		
		cout << "Case #" << test_case + 1 << '\n';
		cout << Answer << '\n';
	}

	return 0;
}

직사각형이 장애물로 존재하는 곳을 굴러가는 원의 자취에 관한 문제였던 것으로 기억한다.

 

자세한 복기는 문제가 공식적으로 업로드된 이후에 하도록 하겠다.

 

#define _USE_MATH_DEFINES
#include <iostream>
#include<math.h>
#include<iomanip>
using namespace std;

double dis;

int main(int argc, char** argv)
{
	
	ios::sync_with_stdio(false);
	cin.tie(0);
	//setbuf(stdout, NULL); //주석풀고제출

	int T, test_case;

	cout << fixed;
	cout.precision(12);


	cin >> T;
	
	for (test_case = 0; test_case < T; test_case++)
	{
		double r, st, en;
		cin >> r >> st >> en;
		double blk;
		cin >> blk;

		dis = 0;

		while (blk--) {
			double lef, rig, h;
			cin >> lef >> rig >> h;
			
			if (h >= r) {
				
				dis = dis + ((lef - r) - st); // 시작점에서 왼쪽가로
				dis = dis + (h - r) * 2;
				dis = dis + (r * M_PI);
				dis = dis + rig - lef;
				st = rig + r; //시작점 이동
			}
			else {
				double temp = sqrt(pow(r, 2) - pow(r - h, 2));
				dis = dis + (lef - temp - st);
				dis = dis + rig - lef;
				double angle = atan2(temp, r - h);
				dis = dis + angle * r * 2 ;
				st = rig + temp;
			}
			
		}
		dis = dis + en - st;

		cout << "Case #" << test_case + 1 << '\n';
		cout << dis << '\n';
	}

	return 0;
}

+ Recent posts