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

 

2539번: 모자이크

수찬이는 선생님을 도와서 교실 벽면을 장식할 모자이크 그림을 그리기로 하였다. 이를 위하여 직사각형 모양의 큰 도화지를 준비하여 교실 벽에 붙이고 1cm 간격으로 가로선과 세로선을 그려서 정사각형 모양의 칸을 만들고, 각 칸마다 같은 색의 물감으로 색칠을 하였다. 그런데 잘못 칠해진 칸이 있음을 발견하게 되었다. 수찬이는 도화지와 색깔이 같은 색종이를 사서 잘못 칠해진 칸에 색종이를 붙이고 다시 그리는 것이 좋겠다고 생각하고 선생님께 상의를 드렸다. 선생

www.acmicpc.net

한국정보올림피아드 2007년도 초등부 3번문제이자 백준 2539번 문제 "모자이크"문제 풀이입니다.

 

2007년도때 초등부 문제는 3문제만 나왔네요. 마지막 문제 3번 문제입니다.

 

문제 분석

2차원 행렬칸에 구멍이 난 녀석이 있고, 정사각형 색종이로 이들을 모두 가려야 합니다.

 

정사각형 색종이의 개수는 정해져 있고, 겹쳐서 덮어도 됩니다. 정사각형 색종이의 최소 크기를 구하는 문제입니다.

 

뭔가 나름 어려운 문제 일 수도 있는데, 한 가지 조건 덕에 난이도가 꽤 낮아진 감이 있습니다.

 

바로 "모든 색종이는 반드시 도화지의 밑변에 맞추어 붙인다." 라는 조건이죠. 밑 변에 맞추어서 붙여야 한다는 조건이 있습니다.

 

 

행과 열 개수는 100만 미만이라고 하니, 이들을 모두 2차원 배열로 잡기는 조금 무리가 있어 보입니다.

 

알고리즘 구상

일단 모든 색종이는 도화지의 밑변에 맞추어 붙어야 한다는 조건 때문에, 구멍이 난녀석의 y좌표가 가장 높은 녀석을 커버할 수 있어야 합니다.

 

따라서 색종이의 크기 최소값은, 가장 큰 y좌표 값 보다는 같거나 커야 합니다.

 

그리고 모든 점을 덮을 최소 색종이 크기를 알아내야하는데, 이는 파라메트릭 서치로 풀 수 있습니다.

 

문제를 다음과 같이 환원합니다.

 

//(f(x)//) => 색종이 크기가 x일 때 주어진 개수의 색종이로 모든 점을 덮을 수 있는가?

 

만약 크기가 x일때 모두 덮을 수 있다면, 당연히 x+1의 크기로도 덮을 수 있겠지요.

 

만약 y크기로 덮을 수 없다면, y-1의 크기로도 덮을 수 없습니다.

 

이러한 성질을 이용하여 파라메트릭 서치(바이너리 서치)로 풀 수 있습니다.

 

 

//(f(x)//)는 그리디하게 구할 수 있습니다.

 

구멍의 x좌표들만 모아놓은 상태에서, 처음 만나는 점을 색종이의 가장 왼쪽 변에 맞게 놓는다고 치고,

 

지금 놓여진 색종이의 오른쪽 변을 넘어가면 새로운 색종이를 덮는 식으로, 총 색종이가 몇 장 필요한지 알아내서

 

주어진 색종이 개수로 덮을 수 있는지를 확인하면 됩니다.

 

따라서 //(f(x)//)는 //(O(N)//)만에 구할 수 있고, 여기서 //(N//)는 잘못 칠해진 색종이 칸의 개수이므로 1000이 됩니다.

계산량은 총 //(1000 * lg1000000//)이므로, 쉽게 구할 수 있습니다.

 

코드

더보기
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
vector<int> arr;
int need(int width) {
	// 색종이 폭이 width일때, 다 덮기 위해 필요한 종이의 수
	int prev = -1;
	int ret = 0;
	for (int pos : arr) {
		if (prev == -1) {
			//처음 종이를 놓는 경우, [prev, prev+width) 까지 커버 가능
			prev = pos;
			ret++;
		}
		else if (prev + width <= pos) {
			prev = pos;
			ret++;
		}
	}
	return ret;
}
int main() {
	int row, col, num_paper, num_hole;
	int max_height = 0;
	cin >> row >> col >> num_paper >> num_hole;
	for (int i = 0; i < num_hole; i++) {
		int x, y; //행, 열
		cin >> x >> y;
		max_height = max(max_height, x);
		arr.push_back(y);
	}
	sort(arr.begin(), arr.end());
	int l = max_height;
	int r = 1000000; //range = [l, r]
	while (l < r) {
		int m = (l + r) / 2;
		if (need(m) <= num_paper) {
			//가능한 경우
			r = m;
		}
		else {
			l = m + 1;
		}
	}
	cout << l << endl;
	return 0;
}

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

 

1713번: 후보 추천하기

첫째 줄에는 사진틀의 개수 N이 주어진다. (1≤N≤20) 둘째 줄에는 전체 학생의 총 추천 횟수가 주어지고, 셋째 줄에는 추천받은 학생을 나타내는 번호가 빈 칸을 사이에 두고 추천받은 순서대로 주어진다. 총 추천 횟수는 1,000번 이하이며 학생을 나타내는 번호는 1부터 100까지의 자연수이다.

www.acmicpc.net

한국정보올림피아드 초등부 2007년도 전국본선 1번문제이자 백준 1713번 문제 "후보 추천하기" 문제 풀이입니다.

 

문제 개요

정올 초등부 1번문제 치고는 solved.ac 티어가 골드5 로 높은 편입니다.

문제 내용을 보면, 약간 특이한 룰을 따라서 코딩을 해야 합니다. 시뮬레이션 종류의 문제로 볼 수 있습니다.

 

LRU Cache 구현하는 것 같은 느낌이고, 사진 틀에 학생 사진을 넣고 빼는 것을 잘 시뮬레이션 해야 합니다.

 

특히나 중요한 부분은 사진 틀이 꽉 찼을 때 어떤 녀석을 빼느냐에 관한 문제인 것 같은데, 사진 틀의 개수가 최대 20개 밖에 되지 않으니, 뺄 녀석을 그때 그때 모든 사진 틀을 다 순회하면서 체크하면 될 것 같습니다.

 

총 추천회수는 1000이니, 최악의 경우 계산량은 //(1000*20//)으로 2만 정도가 됩니다.

시간복잡도는 크게 문제가 되지는 않을 것 같습니다.

 

사진 틀에서 삭제되는 경우, 추천 수가 0이 되는 것은 사진틀에서 뺀 경우 추천 수를 트래킹 하지 않으면 되므로 구현을 더 간단하게 해 줍니다.

 

구현전략


사진틀에서 가장 추천 수가 적거나, 추천수가 같다면 더 오래된 녀석을 빼야 한다." 이 조건을 쉽게 하도록 구현을 해야하는데, pair<int, int>를 사용하면 편할 듯 합니다.

 

C++ STL에 있는 pair은 비교 연산자가 기본적으로 first끼리 비교하고, first가 같다면 second끼리 비교하는 탬플릿 구조체입니다.

template class<T1, T2>
class pair {
	T1 first;
    T2 second;
    bool operator<(const class pair& rhs) const {
    	if (first != rhs.first) {
        	return first < rhs.first;
        }
        return second < rhs.second;
    };
    bool operator>(const class pair& rhs) const {
    	if (first != rhs.first) {
        	return first > rhs.first;
        }
        return second > rhs.second;
    }
};

대략 위와 같은 형태라고 보시면 됩니다.

 

따라서 first에 투표 수, second에 추가한 시기를 넣으면, pair값이 최소인녀석을 제거하면 되는 형식입니다.

 

코드

더보기

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
map<int, pii> slot; //pii.first = vote , pii.second = age

int main() {
	int n, k;
	int cnt = 0;
	cin >> n >> k;
	for (int i = 1; i <= k; i++) {
		int tmp;
		cin >> tmp;
		(void)0;
		if (slot.find(tmp) == slot.end()) {
			//기존에 없는 경우
			if (slot.size() < n) {
				slot[tmp].first = 1;
				slot[tmp].second = i;
			}
			else {
				//기존거 지우고
				int victim = slot.begin()->first;
				pii value = slot.begin()->second;
				for (auto a : slot) {
					if (a.second < value) {
						value = a.second;
						victim = a.first;
					}
				}
				slot.erase(victim);
				slot[tmp].first = 1;
				slot[tmp].second = i;
			}
		}
		else {
			//있는 경우 투표만 추가시킨다.
			slot[tmp].first = slot[tmp].first + 1;
		}
	}
	for (auto it : slot) {
		printf("%d ", it.first);
	}
	return 0;
}

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

 

6988번: 타일 밟기

첫째 줄에는 타일의 개수 N이 주어진다. N은 3 이상 3,000 이하이다. 둘째 줄에는 N개의 타일에 적힌 자연수들이 증가하는 순서로 빈칸을 사이에 두고 주어진다. 타일에 적힌 자연수들은 각각 1,000,000 이하이다.

www.acmicpc.net

백준 6988번 문제이자, 정보올림피아드 초등부 2008년도 전국본선 3번문제 타일 밟기 문제이다.

 

문제 분석

타일이 최대 3000개가 주어지고, 타일 번호는 정렬되어 있다.

 

최소 3개 이상 연속된 타일의 숫자 합 중 최대값을 구해야 하고, 연속된 타일이라 함은

타일들 간에 값의 차이가 일정해야 한다는 것이다.

 

등차수열을 이루는 타일 값들의 합이 최대여야 한다는 것

 

타일에 적힌 수는 100만까지 나올 수 있다.

 

알고리즘 설계 및 시간복잡도 분석

타일에 적힌 수는 자연수이므로 1부터 100만의 범위이다.

그러면 타일 간 값의 차이는 1부터 50만 정도가 된다고 볼 수 있다 대충

1 50만 100만1 이렇게 하면 3개가 된다.

 

즉 1 500000 1000001 이렇게 인데, 사실 100만1은 안나오지만, 대충 타일 값 차이가 50만 가까이가

최대값이라고 보면 된다.

 

따라서 타일간 차이값인 diff값을 모두 다 찾아보면서 순회하려면 diff값이 50만개가 될 것이고

 

diff값 별 타일 모두를 다 훑어야 하므로 for-loop 도는 횟수는 50만 * 3천 정도 인데

 

이 값은 15억 정도 되므로, 15초 이상 걸리는 알고리즘 이라고 볼 수있다. 고로 Time Limit Exceed가

자명한 알고리즘이라는 뜻

 

그러면 이 계산량을 줄여야 하는데, N값이 3000이므로 꽤나 작다.

 

3000이라면 //(O(N^2)//)알고리즘을 짜도 돌아간다는 뜻.

 

 

여기서 잘 모르겠어서 아래의 알고리즘 분류를 "보기"를 눌러서 확인해 보았는데

다이나믹 프로그래밍이라고 힌트를 얻었다.

 

그러면 N값을 기준으로 메모이제이션 배열을 활용하면 될 것 같은데, N크기에 맞는 2차원 디피까지는 가능할 것 같다.

 

그렇다면 dp 배열 인덱스에 diff값을 넣을 수는 없다. 메모리 문제도 있고 시간 문제도 있고

 

그래서 //(dp[3000][3000]//)식으로 잡고, //(dp[i][j]//)라 했을 때, //(i//)가 마지막 인덱스고 //(j//)가 직전 인덱스로 해서

 

//(arr[i] - arr[j]//)로 diff값을 구할 수 있으니, 이를 활용해 보기로 했다.

 

dp[i][j] = i번째에서 끝나고, 그 직전에 arr[j]의 값을 갖는 시퀀스 합의 최대값

이라고 정의해보자.

 

처음 3개짜리만 대상으로 dp배열을 모두 전처리 해 놓은 뒤,

모든 dp배열을 돌면서, arr[j], arr[i]를 통해서 다음에 나올 수 있는 값을 구해서, 그 값이 존재하는 지

확인한 뒤, 존재한다면 그 인덱스를 //(k//)라 하고, //(dp[k][i]//)에 //(dp[i][j] + arr[k]//)를 넣는 식으로 구하면 될 듯하다.

 

값이 존재하는지는, 값의 범위가 1부터 100만이므로 배열을 잡기 보다는 c++ stl map같은 자료구조를 쓰면 좋을 것 같다.

배열로 잡는다면 값 자체는 3000개라서 sparse한 배열이 될 것이고, 메모리가 터질 수 있으므로

 

구현 코드

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
int arr[3005];
map<int, int> mm;
ll dp[3005][3005]; // dp[i][j] => i번째에서 끝나고, 이전이 j인 시퀀스의 최대 누적합
int main() {
	ll ans = 0;
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		int tmp;
		cin >> tmp;
		arr[i] = tmp;
		mm[tmp] = i;
	}
	for (int i = 1; i < n; i++) {
		int cur = arr[i];
		for (int j = 0; j < i; j++) {
			int prev = arr[j];
			ll next = cur + (cur - prev);
			if (mm.find(next) != mm.end()) {
				int next_idx = mm[next];
				dp[next_idx][i] = cur + prev + next;
				ans = max(ans, dp[next_idx][i]);
			}
		}
	}
	for (int i = 1; i < n; i++) {
		int cur = arr[i];
		for (int j = 0; j < i; j++) {
			if (dp[i][j]) {
				int prev = arr[j];
				ll next = cur + cur - prev;
				if (mm.find(next) != mm.end()) {
					int next_idx = mm[next];
					dp[next_idx][i] = dp[i][j] + next;
					ans = max(ans, dp[next_idx][i]);
				}
			}
		}
	}
	cout << ans << endl;

	return 0;
}

 

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

 

6986번: 절사평균

첫째 줄에 절사평균(N, K)를, 둘째 줄에 보정평균(N, K)를 각각 소수점이하 셋째 자리에서 반올림하여 둘째 자리까지 출력한다. 예를 들어 결과값이 9.667인 경우 9.67로, 5인 경우 5.00으로, 5.5인 경우에는 5.50으로 출력한다.

www.acmicpc.net

문제 내용

 

2008년도 초등부 문제 풀어보다가 1번 문제라서 쉽겠거니 하고 했다가 우수수 하고 틀려버렸다 ㅡㅡ

 

문제 내용은 크게 어려운 것은 없다.

 

규칙에 맞게 절사평균과 보정 평균을 구해주면 되는데, 배열로 받은 뒤 소팅해서

 

앞에 k개 만큼을 날리던지, 근처 값으로 변경해주던지 하면 된다.

 

 

하 그놈의 Floating Point 정확도 문제

 

Floating Point precision 문제때문에 하....

 

 

일단 입력값 중 점수는 0 ~ 10범위의 실수에 소숫점 한자리까지 나오는 floating point이고

전채 개수가 10만개 까지 나올 수 있다.

 

10만개라는 이유 때문에 precision 문제가 발생할 수 있다.

컴퓨터에서 사용하는 부동소수점 방식은 작은 에러값이 발생할 수 있다.

따라서 이게 누적되면 정확하지 않은 값이 나올 수 있는데

 

 

따라서 정수로 환원해서 풀어야 한다.

 

 

결과값은 소수점 2자리까지 출력해야하고, 이로 인해서 100을 곱하면 되겠지만, 소수점 셋째자리에서

 

반올림을 해야 하므로 1000을 곱해서 소수점 셋째자리까지 일단 표현 가능하게 정수로 받았다.

 

그리고 마지막에 5를 더해서 반올림을 구현했다.

 

출력할때는 1000을 나눈 정수 부분과 1000로 모듈러 연산을 한 소수부분(fraction)을 따로 출력해주었다.

 

5가 정답일 때 5.00라고 출력해야 하므로 포맷 스트링에 0 extension도 해 주어야 한다.

 

%02d와 같이 하면 너비 2만큼 0을 꽉채워서 출력해준다.

 

구현 코드

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
int main() {
	int n, k;
	vector<ll> arr;
	cin >> n >> k;
	arr.resize(n);
	for (int i = 0; i < n; i++) {
		double tmp;
		cin >> tmp;
		arr[i] = (ll)(tmp * 1000);
	}
	sort(arr.begin(), arr.end());
	ll s = 0;
	for (int i = k; i + k < arr.size(); i++) {
		s += arr[i];
	}
	ll ans = s / (arr.size() - k - k) + 5;
	printf("%lld.%02lld\n", ans / 1000, ans % 1000 / 10);
	s = 0;

	s += k * arr[k];
	for (int i = k; i + k < arr.size(); i++) {
		s += arr[i];
	}
	s += k * arr[arr.size() - 1 - k];
	ans = s / arr.size() + 5;
	printf("%lld.%02lld\n", ans / 1000, ans % 1000 / 10);
}

 

https://www.acmicpc.net/category/detail/340

 

KOI 2010 초등부

 

www.acmicpc.net

 

한국정보올림피아드 초등부 2010년도 문제 풀이입니다.

 

총 3문제가 있습니다.

 

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

 

2467번: 용액

첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하의 정수이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 오름차순으로 입력되며, 이 수들은 모두 -1,000,000,000 이상 1,000,000,000 이하이다. N개의 용액들의 특성값은 모두 서로 다르고, 산성 용액만으로나 알칼리성 용액만으로 입력이 주어지는 경우도 있을 수 있다.

www.acmicpc.net

 

1번 용액 (백준 2467)

1번 용액 문제입니다.

 

1번 문제치곤 조금 난이도가 있는 문제라고 생각합니다.

 

처음에는 바이너리 서치를 이용해서 O(NlgN)으로 풀이를 해 보았습니다.

A번째 용액, B번째 용액을 골라서 확인을 하되, A번째 용액에 대해서는 for문 을 돌고, 각각 A번째용액에 대하여, B번째 용액을 결정할 때, 바이너리 서치 개념을 적용해서 풀어보았습니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
int a[100005];
int n;
int f, s, val;
int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", a + i);
	}
	f = a[0];
	s = a[1];
	val = abs(f+s);
	for (int i = 0; i < n - 1; i++) {
		int first = a[i];

		int left = i + 1; // [left, right]
		int right = n - 1;
		while (left < right) {
			int mid = (left + right) / 2;
			int& second = a[mid];
			int sum = first + second;
			if (sum < 0) {
				if (abs(sum) < val) {
					val = abs(sum);
					f = first;
					s = second;
				}
				left = mid + 1;
			}
			else if (sum > 0) {
				if (abs(sum) < val) {
					val = abs(sum);
					f = first;
					s = second;
				}
				right = mid;
			}
			else {
				cout << first << ' ' << a[mid] << endl;
				return 0;
			}
		}
		int second = a[right];
		int sum = first + second;
		if (abs(sum) < val) {
			val = abs(sum);
			f = first;
			s = second;
		}
		second = a[left];
		sum = first + second;
		if (abs(sum) < val) {
			val = abs(sum);
			f = first;
			s = second;
		}
	}
	cout << f << ' ' << s << endl;
	return 0;
}

 

이 풀이 외에도 O(N)으로도 풀이가 가능하다는 것을 알게 되었습니다.

 

배열이 정렬되어 있기 때문에, A번째에서 A+1번째로 이동할 때, 더 0에 가까운 값을 구하기 위해서는 B에서 B-1로 이동하는 경우는 있을 수 있으나 B+1에서 더 0에 가까운 값이 나올 수는 없기에 그리디하게 투포인터 형식으로 풀 수있습니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
int arr[100005];
int main() {
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", arr + i);
	}
	int a, b, val;
	a = arr[0], b = arr[n - 1];
	val = abs(a + b);
	int left = 0;
	int right = n - 1;
	while (left < right) {
		int l, r, sum;
		l = arr[left];
		r = arr[right];
		sum = l + r;
		if (abs(sum) < val) {
			val = abs(sum);
			a = l;
			b = r;
		}
		if (sum > 0) {
			right--;
		}
		else {
			left++;
		}
	}
	printf("%d %d\n", a, b);
}

코드도 훨씬 간단합니다.

 

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

 

2468번: 안전 영역

재난방재청에서는 많은 비가 내리는 장마철에 대비해서 다음과 같은 일을 계획하고 있다. 먼저 어떤 지역의 높이 정보를 파악한다. 그 다음에 그 지역에 많은 비가 내렸을 때 물에 잠기지 않는 안전한 영역이 최대로 몇 개가 만들어 지는 지를 조사하려고 한다. 이때, 문제를 간단하게 하기 위하여, 장마철에 내리는 비의 양에 따라 일정한 높이 이하의 모든 지점은 물에 잠긴다고 가정한다. 어떤 지역의 높이 정보는 행과 열의 크기가 각각 N인 2차원 배열 형태로 주어

www.acmicpc.net

 

2번 안전영역 (백준 2468)

2번 문제 안전영역입니다.

 

N값이 100으로 별로 크지 않으므로, BFS혹은 DFS로 Flood-Fill해서 안전지역 개수를 세면 되고, 높이 값이 1~100이므로, 100번 해 주면 됩니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
int field[101][101];
bool visit[101][101];
int n;
bool isin(int x, int y) {
	return x >= 0 && y >= 0 && x < n && y < n;
}
const int dx[] = { +1, -1, +0, +0 };
const int dy[] = { +0, +0, +1, -1 };
void dfs(int x, int y, int h) {
	//visit[x][y] = true;
	for (int k = 0; k < 4; k++) {
		int nx = x + dx[k];
		int ny = y + dy[k];
		if (isin(nx, ny) && field[nx][ny] > h) {
			if (visit[nx][ny] == false) {
				visit[nx][ny] = true;
				dfs(nx, ny, h);
			}
		}
	}
}
int check(int h) {
	int ret = 0;
	memset(visit, 0, sizeof(visit));
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (visit[i][j] == false && field[i][j] > h) {
				visit[i][j] = true;
				ret++;
				dfs(i, j, h);
			}
		}
	}
	return ret;
}
int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			cin >> field[i][j];
		}
	}
	int ans = 1;
	for (int i = 1; i <= 101; i++) {
		ans = max(ans, check(i));
	}
	cout << ans << endl;
	return 0;
}

 

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

 

2469번: 사다리 타기

첫 줄에는 참가한 사람의 수 k가 나온다(3≤k≤26). 그 다음 줄에는 가로 막대가 놓일 전체 가로 줄의 수를 나타내는 n이 나온다(3≤n≤1,000). 그리고 세 번째 줄에는 사다리를 타고 난 후 결정된 참가자들의 최종 순서가 길이 k인 대문자 문자열로 들어온다.   k와 n, 그리고 도착순서 문자열이 나타난 다음, 이어지는 n개의 줄에는 앞서 설명한 바와 같이 ‘*’와 ‘-’ 문자로 이루어진 길이 k-1인 문자열이 주어진다. 그 중 감추어진 가로 줄

www.acmicpc.net

3번 사다리 타기 (백준 2469)

3번 사다리 타기 문제입니다.

 

사다리를 타서 모양을 맞출 수 있느냐와 관련된 문제입니다.

 

양방향 dfs의 개념을 적용하면 간단하게 풀어 볼 수 있습니다.

Meet in the middle 이라는 개념과도 좀 비슷할 수 있습니다.

 

시작 시 알파뱃 배치와 종료시 알파뱃 배치 둘다 알기 때문에, 위에서 내려오고 아래에서 올라와서 모르는 사다리 배치 전후의 배열을 알아냅니다.

 

그리고 그 전 후 배열을 사다리 한 줄로 적용해서 변환 가능한지에 따라 풀어내면 됩니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
void fail(int k) {
	while (--k) {
		cout << 'x';
	}
	cout << endl;
	exit(EXIT_SUCCESS);
}
int main() {
	string lines[1005];
	int k, n;
	int q;
	cin >> k >> n;
	string first;
	string fin;
	string ans = "";
	cin >> fin;
	for (int i = 0; i < k; i++) {
		first += ('A' + i);
	}
	for (int i = 0; i < n; i++) {
		cin >> lines[i];
		if (lines[i][0] == '?') q = i;
	}
	for (int i = 0; i < q; i++) {
		for (int j = 0; j < lines[i].length(); j++) {
			char& ch = lines[i][j];
			if (ch == '-') {
				swap(first[j], first[j + 1]);
			}
		}
	}
	for (int i = n - 1; i > q; i--) {
		for (int j = 0; j < lines[i].length(); j++) {
			char& ch = lines[i][j];
			if (ch == '-') {
				swap(fin[j], fin[j + 1]);
			}
		}
	}

	for (int i = 0; i < k - 1; i++) {
		char up = first[i];
		char down = fin[i];
		if (up != down) {
			ans += "-";
			if (i + 2 < k) {
				ans += "*";
				i++;
			}
		}
		else {
			ans += "*";
		}
	}
	for (int i = 0; i < ans.length(); i++) {
		if (ans[i] == '-') {
			swap(first[i], first[i + 1]);
		}
	}
	if (first != fin) fail(k);
	cout << ans << endl;
}

한국정보올림피아드 (KOI) 2018년도 초등부 문제 풀이입니다.

https://www.acmicpc.net/category/detail/1894

 

KOI 2018 초등부

 

www.acmicpc.net

 

백준저지에서 문제를 확인 / 풀이가 가능합니다.

 

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

 

15969번: 행복

모든 서브태스크에서 2 ≤ N ≤ 1,000이고 입력되는 학생들의 점수는 0 이상 1,000 이하의 정수이다.

www.acmicpc.net

1번 행복 (백준 15969)

1번 행복 문제입니다.

 

아주 간단한 문제이죠. 최대값과 최소값을 구해서 뺀 뒤 출력하면 됩니다.

 

파이썬2으로 풀어보았습니다.

 

input();a=map(int,raw_input().split());print max(a)-min(a)

 

 

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

 

15970번: 화살표 그리기

직선 위에 위치를 나타내는 0, 1, 2, ...와 같은 음수가 아닌 정수들이 일정한 간격으로 오른쪽 방향으로 놓여 있다. 이러한 위치들 중 N개의 위치에 하나씩 점들이 주어진다(<그림 1>). 주어진 점들의 위치는 모두 다르다. 두 점 사이의 거리는 두 점의 위치를 나타내는 수들의 차이이다. <그림 1>에서는 4개의 점이 주어지고 점 a와 b의 거리는 3이다. <그림 1> 각 점은 N개의 색깔 중 하나를 가진다. 편의상, 색깔은 1부터 N까지의 수로 표

www.acmicpc.net

2번 화살표 그리기 (백준 15970)

2번 화살표 그리기 문제입니다.

 

그리디하게 가장 가까운 녀석들에 화살표를 잇고 그 길이를 모두 더하면 되는 문제입니다.

색상이 흑과 백, 2가지만 나오지 않고 N가지가 나온다는 점에 유의 하여

 

색깔별로 다른 vector에 저장한 뒤, 정렬하여 인접한 녀석 중 가까운 곳에 잇는 방식으로 풀 수 있습니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
vector<int> A[5005];
int main() {
	int N;
	cin >> N;
	for (int i = 0; i < N; i++) {
		int pos, col;
		cin >> pos >> col;
		A[col-1].push_back(pos);
	}
	for (int i = 0; i < N; i++) {
		sort(A[i].begin(), A[i].end());
	}
	int ans = 0;
	for (int j = 0; j < N; j++) {
		for (int i = 0; i < A[j].size(); i++) {
			int prev, next;
			prev = next = 987654321;
			if (i > 0) {
				prev = A[j][i] - A[j][i - 1];
			}
			if (i + 1 < A[j].size()) {
				next = A[j][i + 1] - A[j][i];
			}
			ans += min(prev, next);
		}
	}
	cout << ans << endl;
	return 0;
}

 

 

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

 

15971번: 두 로봇

입력에서 두 번째 줄에 주어지는 방번호는 1과 2, 세 번째 줄에 주어지는 방 번호는 2와 3, …, i번째 줄에 주어지는 방 번호는 i-1과 i, …, N번째 줄에 주어지는 방 번호는 N-1과 N이다 (아래 입력과 출력의 예에서 예제 입력 1을 참고).

www.acmicpc.net

3번 두 로봇 (백준 15971)

3번 두 로봇 문제입니다.

 

그림만 봐도 Graph 문제인 것을 알 수 있겠죠?

 

 N개의 넓은 방이 존재하며 좁은 통로로 서로 연결되어 있는 것으로 밝혀졌다. N개의 방은 1번부터 N번까지의 번호를 붙여 1번 방, 2번 방, …, N번 방으로 부른다. 통로는 정확히 N-1개가 발견되었는데, 각각 서로 다른 두 방 사이를 연결시켜 주며 중간에 다른 통로와 이어지는 경우는 없다고 한다. 또한 이 통로들을 이용하여 임의의 두 방 사이를 이동하는 것이 가능하며, 임의의 두 방 사이를 이동할 때 같은 통로를 두 번 이상 지나지 않는 경로는 유일한 것으로 밝혀졌다.

 

이 조건을 보아서, 이 Graph는 트리입니다.

 

서로 다른곳에 있는 로봇이 만나기 위해 이동해야 하는 거리의 최소값을 구해야 합니다. 두 점의 최단거리 문제이죠.

 

그리고 한 Edge에 대해서는 이동하지 않고 통신을 할 수 있기 때문에, 그냥 최단거리 문제는 아닙니다.

 

Edge의 Weight값이 균일하지 않기 때문에 Dijkstra Shortest Path 알고리즘으로 풀 수 있으며, 한 Edge의 값은 제거할 수 있기 때문에 지난 Edge 중 가장 큰 Edge weight 값을 가지고 있다가, 총 거리에서 해당 Largest Edge weight를 제거하면 정답이 됩니다.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;

#define MAXN 100005
vector<pii> edges[MAXN];
bool visit[MAXN];

struct State {
	int pos, totallen, maxseg;
	bool operator<(const State& rhs) const {
		return totallen > rhs.totallen;
	}
};
int main() {
	int N, A, B;
	cin >> N >> A >> B;
	for (int i = 1; i < N; i++) {
		int src, dst, len;
		cin >> src >> dst >> len;
		edges[src].push_back(make_pair(dst, len));
		edges[dst].push_back(make_pair(src, len));
	}
	priority_queue<State> pq;
	pq.push({ A, 0, 0 });
	while (pq.size()) {
		State state = pq.top(); pq.pop();
		int pos = state.pos;
		visit[pos] = true;
		if (pos == B) {
			cout << (state.totallen - state.maxseg) << endl;
            return 0;
		}
		for (auto next : edges[pos]) {
			int next_pos = next.first;
			int next_seglen = next.second;
			if (visit[next_pos] == false) {
				pq.push({ next.first, state.totallen + next_seglen, max(state.maxseg, next_seglen) });
			}
		}
	}

}

 

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

 

15972번: 물탱크

세로 길이가 N, 가로 길이가 M, 높이가 H인 물탱크가 있다. N, M, H는 모두 양의 정수이다. <그림 1>은 세로 길이가 2, 가로 길이가 3, 높이가 5인 물탱크 모양을 보여준다. <그림 1>에서 보듯이 물탱크 내부는 가로와 세로로 벽이 설치되어 있는데, 내부 각 칸(즉 사각기둥 모양)의 세로와 가로 길이는 1이고 높이는 H가 되도록 벽이 설치되어 있다. 이 물탱크를 위에서 내려다보면 <그림 2>와 같이 각 칸이 정사각형인 격자 모양이 된다. 물

www.acmicpc.net

4번 물탱크 (백준 15972)

4번 물탱크 문제입니다.

 

아이디어를 생각해내기 좀 어려웠는데요, 풀고 나서도 왜 이게 풀렸는지 지금 100% 이해가 된 상태는 아닙니다.

 

일단 Graph 문제로 환원하여 생각을 하였고, 외부와 직/간접적으로 연결된 녀석들은 물이 셀 수 있습니다.

 

직접적으로 연결된 녀석은 그 연결된 구멍의 높이만큼 물이 세어나가게 되고, 간접적으로 연결된 녀석은 조건에 따라서 물이 세어나갈수도 있고 아닐 수도 있습니다.

 

외부에서 부터 Graph 탐색을 한다고 하였을 때, 간접적으로 외부와 연결된 내부 공간들이 탐색이 되게 되는데, 

 

이때 외부로 연결되는 구멍의 높이가 높아지는 경우와 낮아지는 경우가 있습니다. 

 

외부와 연결되는 구멍의 높이가 높아지는 경우는 최근 구멍의 높이만큼 물이 줄줄 세개 됩니다.

 

 

구멍의 높이가 낮아지는 경우는, 최근 연결된 녀석의 수심만큼만 물이 줄줄 세어나가게 됩니다.

 

이러한 특징을 활용해서 변형된 Dijkstra 최단거리 알고리즘으로 문제를 풀이했습니다.

 

구멍 높이가 낮은 녀석부터 먼저 탐색하도록 하였고, 이게 문제가 풀릴지 아닐지 몰랐는데, 풀리긴 하더군요.

 

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;

struct Edge{
	int x, y, h;
};
struct State {
	int x, y, sx, sy, h;
	bool operator<(const struct State& rhs) const {
		return h > rhs.h;
	}
};
vector<Edge> edges[1005][1005];
int height[1005][1005];
bool visit[1005][1005];

int main() {
	int N, M, H;
	scanf("%d%d%d", &N, &M, &H);

	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= M; j++) {
			height[i][j] = H;
		}
	}

	for (int i = 0; i <= N; i++) {
		for (int j = 1; j <= M; j++) {
			/*
			(i,j) ~ (i+1, j)
			*/
			int v;
			scanf("%d", &v);
			if (~v) {
				edges[i][j].push_back({ i + 1, j, v });
				edges[i + 1][j].push_back({ i, j, v });
			}
		}
	}
	for (int j = 1; j <= N; j++) {
		for (int i = 0; i <= M; i++) {
			/*
			(j, i) ~ (j, i+1)
			*/
			int v;
			scanf("%d", &v);
			if (~v) {
				edges[j][i].push_back({ j, i + 1, v });
				edges[j][i + 1].push_back({ j, i, v });
			}
		}
	}
	priority_queue<State> pq;
	for (int i = 0; i <= M + 1; i++) {
		int x = 0;
		int y = i;
		visit[x][y] = true;
		for (auto next : edges[x][y]) {
			pq.push({ next.x, next.y, x, y, next.h });
		}
	}
	for (int i = 0; i <= M + 1; i++) {
		int x = N + 1;
		int y = i;
		visit[x][y] = true;
		for (auto next : edges[x][y]) {
			pq.push({ next.x, next.y, x, y, next.h });
		}
	}

	for (int i = 1; i <= N; i++) {
		int x = i;
		int y = 0;
		visit[x][y] = true;
		for (auto next : edges[x][y]) {
			pq.push({ next.x, next.y, x, y, next.h });
		}
	}
	for (int i = 1; i <= N; i++) {
		int x = i;
		int y = M + 1;
		visit[x][y] = true;
		for (auto next : edges[x][y]) {
			pq.push({ next.x, next.y, x, y, next.h });
		}
	}

	while (pq.size()) {
		auto state = pq.top(); pq.pop();
		int x = state.x;
		int y = state.y;
		int sx = state.sx;
		int sy = state.sy;
		int h = state.h;
		if (visit[x][y] == false) {
			visit[x][y] = true;
			height[x][y] = min(height[x][y], max(h, height[sx][sy]));
			for (auto eg : edges[x][y]) {
				if (visit[eg.x][eg.y] == false) {
					pq.push({ eg.x, eg.y, x, y, eg.h });
				}
			}
		}
	}

	int ans = 0;
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= M; j++) {
			ans += height[i][j];
		}
	}
	printf("%d\n", ans);

	return 0;
}

한국정보올림피아드(정올) 1996년도 초등부 문제 풀이 포스팅이다.


문제들은 백준저지에서 참고하고 풀이했다.

https://www.acmicpc.net/category/detail/403


1번 단지번호 붙이기 문제이다.

https://www.acmicpc.net/problem/2667 (백준 2667번)


dfs로 풀면 간단하게 풀 수 있는 문제이다.

 

#include <stdio.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

vector<vector<char> > matrix;
int N; //length of matrix edge
int num_of_danji;
int num_of_1;
int x,y;
vector<int> num_of_houses;
void dump_matrix();
bool find_danji();
bool spread(int xpos, int ypos);
bool comp(int a, int b) {
    return (a > b);
};
int main() {
	char temp;
	scanf("%d\n", &N);
	
	matrix.assign(N, vector<char>(N, '0'));
	for (int i=0; i<N; i++) {
		for (int j=0; j<N; j++) {
			scanf("%c", &temp);
			if (temp == '1') {
				num_of_1++;
			}
			matrix[i][j] = temp;
		}
		scanf("\n");
	}

	while (num_of_1 > 0) {
		// dump_matrix();
		find_danji();
		int temp = num_of_1;
		// cout << "x:" << x << endl << "y:" << y << endl;
		spread(x, y);
		num_of_danji++;
		num_of_houses.push_back(temp - num_of_1);
		// dump_matrix();
	}
	
	cout << num_of_danji << endl;
	sort(num_of_houses.begin(), num_of_houses.end());
	for (int i=0; i< num_of_houses.size(); i++) {
		cout << num_of_houses[i] << endl;;
	}
	
}

bool find_danji() {
	for (int i=0; i<N; i++) {
		for (int j=0;j<N; j++) {
			if (matrix[i][j] == '1') {
				x = i;
				y = j;
				return true;
			}
		}
	}
	x = -1;
	y = -1;
	return false;
}
bool spread(int xpos, int ypos) {
	// cout << "spread(" << xpos << "," << ypos << ")" << endl;
	if (xpos >= 0 && xpos < N && ypos >= 0 && ypos < N) {
		// cout << "flag 1"<< endl;
		if (matrix[xpos][ypos] == '1') {
			// cout << "flag 2"<< endl;
			matrix[xpos][ypos] = '0';
			num_of_1--;
			
			return spread(xpos-1, ypos) || spread(xpos, ypos-1) || spread(xpos+1, ypos) || spread(xpos, ypos+1);
		} else {
			return false;
		}
	} else {
		return false;
	}
}
void dump_matrix() {
	for(int i=0; i<N; i++) {
		for(int j=0; j<N; j++) {
			printf("%c", matrix[i][j]);
		}
		putchar('\n');
	}
}




2번 숫자고르기 문제이다.

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

다음 특징들에 착안해서 알고리즘을 고안했다.

위쪽의 그룹의 숫자는 중복이 없고 인덱스값과 같다.

아래쪽 그룹의 숫자는 중복이 있다. 따라서 없는 숫자도 있다.


위쪽 그룹 숫자를 U집합 이라고 하고 아래쪽 집합 숫자를 D집합이라고 하자.


아래쪽 그룹에 없는 숫자가 위쪽 그룹에서 지정될 수 없다는 점을 이용해서 다음 알고리즘을 고안했다.


1. 두번째 줄의 숫자 집합을 구한다. 이를 A라고 한다.

2. A의 원소를 인덱스(첫번째 줄의 숫자기준)로 하는 숫자 집합 B를 구한다.

3. A집합과 B집합이 일치하면 이(A집합 혹은 B집합)를 리턴한다. 그렇지 않으면 다음 단계로 간다.

4. B집합을 A집합으로 두고, B집합은 초기화한다. A집합을 기준으로 스텝 2를 다시 시도한 뒤 3번으로 간다.


백준에 나타난 예시에서 해당 알고리즘을 진행하면 다음과 같이 나타난다.


첫번째 스탭에서 집합 A는 다음과 같이 나타난다.

A = { 1, 3, 4, 5, 6 }

아래쪽 집합의 숫자들을 중복을 제거한 집합의 결과이다.

이때 이 A집합은 U집합에서 고른녀석들이라고 볼 수 있다.


알고리즘 두번째 스탭에서 집합 B를 구한다.

집합 B는 다음 항목들의 집합이다.

D[1], D[3], D[4], D[5], D[6]

B = { 3, 1, 5, 5, 6 } = { 1, 3, 5, 6 }

이때 이 B집합은 D집합에서 A집합에 대응되는 녀석이라고 볼 수 있다.


세번재 스탭에서 A와 B집합을 비교했을 때,  B집합이 크기가 더 작으므로 B집합을 A집합에 대입하고 다시 이어나간다.


A집합은 다시 다음값이 된다.(U집합에서 고른녀석들)

A = { 1, 3, 5, 6 }


B = { 3, 1, 5, 4 } = { 1, 3, 4, 5 }

다시 비교했을 때 맞지 않으므로 A집합에 다시 B 집합을 대응한다.


A = { 1, 3, 4, 5 }

B = { 3, 1, 5, 5 } = { 1, 3, 5 }


일치하지 않으므로 다시 대응한다.

A = { 1, 3, 5 }

B = { 1, 3, 5 }

일치하므로 정답을 리턴한다.


 

#include <stdio.h>
#include <vector>
#include <iostream>
#include <set>

using namespace std;

vector<int> arr; //기본애들
vector<int> elite; //뽑힌애들
set<int> set1, set2;
int N;

void arr_dump();
void set_dump(set<int> a);
bool is_equal(set<int>, set<int>);
int main() {
	
	int temp;
	scanf("%d\n", &N);
	arr.reserve(N+1);
	arr.push_back(0000);
	for (int i=0; i<N; i++) {
		scanf("%d\n", &temp);
		arr.push_back(temp);
	}
	
	// arr_dump();
	for(int i=1; i< arr.size(); i++) {
		set1.insert(arr[i]);
	}
	// cout << "set1 : ";
	// set_dump(set1);
	for (set<int>::iterator pos = set1.begin(); pos != set1.end(); pos++) {
		set2.insert(arr[*pos]);
	}
	// cout << "set2 : ";
	// set_dump(set2);
	// cout << "is_equal " << is_equal(set1, set2) << endl;
	

	do {
		set1 = set2;
		set2.clear();
		for (set<int>::iterator pos = set1.begin(); pos != set1.end(); pos++) {
			set2.insert(arr[*pos]);
		}
		
	} while(!is_equal(set1, set2));
	// cout << "fin";
	// set_dump(set1);
	printf("%lu", set1.size());
	set_dump(set1);
}

void arr_dump() {
	for (int i=1; i<arr.size(); i++) {
		printf("%d ", arr[i]);
	}
}
void set_dump(set<int> a) {
	cout << endl;
	for (set<int>::iterator IterPos = a.begin(); IterPos != a.end(); IterPos++) {
		cout << *IterPos << endl;
	}
}
bool is_equal(set<int> a, set<int> b) {
	if (a.size() != b.size()) {
		return false;
	}
	set<int>::iterator ai, bi;
	ai = a.begin();
	bi = b.begin();
	while (ai != a.end()) {
		if (*ai != *bi) {
			return false;
		}
		ai++;
		bi++;
	}
	return true;
}


3번 직사각형 네개의 합집합의 면적 구하기 문제이다.

https://www.acmicpc.net/problem/2669 (백준 2669번)


N값이 크지 않으므로 좌표평면을 만들어서 죄다 더해버리면 된다.


 

#include <vector>
#include <iostream>
#include <stdio.h>

using namespace std;

int map[100][100] = {0,};
int rect[4][4];

void dump();
int answer();
int main() {
	for (int i=0; i<4;i++) {
		scanf("%d %d %d %d\n", &rect[i][0], &rect[i][1], &rect[i][2], &rect[i][3]);
	}
	for (int i=0; i<4;i++) {
		for (int x=rect[i][0]; x < rect[i][2]; x++) {
			for(int y=rect[i][1]; y < rect[i][3]; y++) {
				map[x][y]++;
			}
		}
	}
	// dump();
	cout << answer() << endl;
}
int answer() {
	int count = 0;
	for(int i=0;i<100;i++) {
		for (int j=0;j<100;j++) {
			if (map[i][j]) {
				count++;
			}
		}
	}
	return count;
}
void dump() {
	//rect
	// for(int i=0;i<4;i++) {
	// 	for(int j=0;j<4;j++) {
	// 		printf("%d\t", rect[i][j]);
	// 	}
	// }
	
	for(int i=99;i>=0;i--) {
		for(int j=99;j>=0;j--) {
			printf("%d ", map[j][i]);
		}
		putchar('\n');
	}
}



한국정보올림피아드(정올) 2013년도 초등부 문제 풀이 포스팅입니다.


문제들은 백준저지에서 참고하고 풀이했습니다.

https://www.acmicpc.net/category/detail/1075


총 4문제가 있습니다.


1번 올림픽 문제입니다. (백준 8979번)

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


단순 계산으로 풀 수 있다. 비교 연산자를 재정의해서 정렬한 뒤, 중복 순위의 경우에만 체크를 해주면 된다.

 

#include <bits/stdc++.h>
using namespace std;
struct data {
    int num, gold, silver, bronze;
    
    bool operator < (const data& rhs) const {
        return gold != rhs.gold ? gold > rhs.gold : (silver != rhs.silver ? silver > rhs.silver : bronze > rhs.bronze);
    }
    bool operator == (const data& rhs) const {
        return gold == rhs.gold && silver == rhs.silver && bronze == rhs.bronze;
    }
};
vector<data> arr;
int main() {
    int n, k;
    cin >> n >> k;
    arr.reserve(n);
    for (int i=0; i < n; i++) {
        data tmp;
        scanf("%d %d %d %d\n", &tmp.num, &tmp.gold, &tmp.silver, &tmp.bronze);
        arr.push_back(tmp);
    }
    sort(arr.begin(), arr.end());
    
    int rank = 0;
    int accu = 1;
    data prev;
    prev.gold = prev.silver = prev.bronze = -1;
    //dump
    for (int i=0; i < n; i++) {
        auto& a = arr[i];
        if ( (prev == a) == false) {
            rank += accu;
            accu = 1;
        } else {
            accu++;
        }
        // printf("rank %d - ", rank);
        if (a.num == k) {
            cout << rank << '\n';
            return 0;
        }
        
        prev = a;
        // printf("(%d) %d, %d, %d\n", a.num, a.gold, a.silver, a.bronze);
    }
    //dump
}


2번 택배 문제입니다.

https://www.acmicpc.net/problem/8980 (백준 8980번)


그리디로 풀 수 있다. 그리디도 그리딘데, 어떤식으로 그리디를 적용시킬지가 중요하다. 일단 배달 구간이 가장 짧은놈들 순서대로 sorting한다. 배달 구간이 짧을수록 트럭에서 공간을 점유하는 시간이 작아지므로 그렇게 하는 것이다.    

그리고 배달구간이 짧은놈 순서대로 트럭에 최대한 집어넣으면 정답이 된다. 이것을 어떻게 구현하느냐에서 많이 해맸는데, 궂이 시간순대로 트럭이 이동하는 방식으로 시뮬레이션을 하지 않더라도 짧은 구간부터, 구간별 트럭에 적제된 화물양을 저장해놓았다가 최대한 적재시키는 식으로 정답을 구하면 쉽게 풀이가 된다.


 

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

typedef pair<int, int> pii; //src, dst
struct Order {
    int src, dst, size;
};

Order orders[10001];

bool cmp(const struct Order& lhs, const struct Order& rhs) {
    if (lhs.dst != rhs.dst) return lhs.dst < rhs.dst;
    return lhs.src < rhs.src;
}
int loaded[2001]; //해당 위치에 있을 때, 트럭에 차있는 양
int main() {
    int ans = 0;
    int n, c, m;
    cin >> n >> c >> m;
    for (int i=0; i < m; i++) {
        scanf("%d %d %d", &orders[i].src, &orders[i].dst, &orders[i].size);
        
    }
    
    sort(orders, orders + m, cmp);
    for (int i=0; i< m; i++) {
        auto& order = orders[i];
        int already_loaded_amount = 0;
        for (int pos = order.src; pos < order.dst; pos++) { //구간을 돌면서 가장 많이 적제된 양을 찾는다.
            if (loaded[pos] > already_loaded_amount) {
                already_loaded_amount = loaded[pos];   
            }
        }
        int can_be_more_load_amt = min(order.size, c - already_loaded_amount); //더 적재할 양을 고른다.
        for (int pos = order.src; pos < order.dst; pos++) { //구간에 적재 시킨다.
            loaded[pos] += can_be_more_load_amt;
        }
        ans += can_be_more_load_amt;
    }    
    cout << ans <<'\n';
    return 0;
}


3번 입력숫자 문제입니다.

https://www.acmicpc.net/problem/8981 (백준 8981번)


비슷한 로직으로 하면 역연산이 된다. 이전 값 만큼 offset을 앞으로 전진시키고, 해당 위치에 다음 받은 값을 쓴다. 만약 거기 이미 값이 있다면 값이 없을 때 까지 앞으로 전진하면 된다.

 

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

int arr[30];
int input[30];
int ptr;
int val;
int n;
void search() {
    while(arr[ptr]) {
        ptr++;
        ptr %= n;
    }
}
int main() {
    cin >> n;
    cin >> val;
    arr[ptr] = val;
    for (int i=1; i < n; i++) {
        ptr += val;
        ptr %= n;
        search();
        cin >> input[i];
        arr[ptr] = val = input[i];
    }
    
    cout << n << endl;
    for (int i=0; i < n; i++) {
        cout << arr[i] << ' ';
    }
    cout << '\n';
	return 0;
}




한국정보올림피아드(정올) 2014년도 초등부 문제 풀이 포스팅입니다.


문제들은 백준저지에서 참고하고 풀이했습니다.

https://www.acmicpc.net/category/detail/1274


총 4문제가 있습니다.


1번 전자레인지 문제입니다. (백준 10162)

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


나눗셈 연산과 모듈러 연산을 이용하면 간단하게 해결할 수 있습니다.



 

#include <iostream>

using namespace std;

int main() {
	int t;
	cin >> t;
	if (t % 10 != 0) {
		cout << -1 << endl;
		return 0;
	}
	int m300, m60, m10;
	m300 = m60 = m10 = 0;
	
	m300 = t/300;
	t %= 300;
	m60 = t/60;
	t %= 60;
	m10 = t/10;
	cout << m300 << ' ' << m60 << ' ' << m10 << endl;
	return 0;
}


2번 색종이 문제입니다. (백준 10163)

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


N값이 작으므로, 2차원 배열로 종이 그리드를 모두 나타내고 시뮬레이션 한 뒤 개수를 세면 됩니다.



 

#include <iostream>

using namespace std;

int map[102][102];
int sz[101];
int n;
int main() {
	cin >> n;
	int sx, sy, dx, dy;
	for (int i=1; i <= n; i++) {
		cin >> sx >> sy >> dx >> dy;
		dx += sx;
		dy += sy;
		for (int x=sx; x < dx; x++) {
			for (int y=sy; y <dy; y++) {
				map[x][y] = i;
			}
		}
	}
	
	for (int i=0; i < 101; i++) {
		for (int j=0; j < 101; j++) {
			if (map[i][j]) {
				sz[ map[i][j] ]++;
			}
		}
	}
	for (int i=1; i <= n; i++) {
		cout << sz[i] << endl;
	}
}


3번 격자상의 경로 문제입니다. (백준 10164)

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


다이나믹 프로그래밍으로 문제를 해결할 수 있습니다. K값이 하나 뿐이므로, (시작점 -> K까지 경로)와 (K지점 -> 도착점까지 경로)를 나누어서 구해주면 된다. 숫자를 1부터 세므로 이에 따른 오류를 조심해야 한다.


get함수를 쓸 때 dp[x][y]는 시작 지점에서 (x, y)까지 도달 할 수 있는 경로의 개수를 뜻한다.

get2 함수를 쓸 때에는, (limitx, limity)에서 (x, y)로 도달할 수 있는 경로의 개수를 뜻한다.

get함수가 호출되었을 때에는 K지점을 기점으로 왼쪽과 위쪽부분의 부분 사각형에만 값이 들어가 있으며 또한 get2함수에서 (limitx, limity)보다 위쪽이나 왼쪽은 0으로 예외처리가 되어있게 처리되어있다.


 

#include <iostream>

using namespace std;
int dp[16][16];

int n, m, k;

int get(int x, int y) {
	if (x < 0 || y < 0) return 0;
	if (x == 0 && y == 0) return 1;
	if (dp[x][y]) return dp[x][y];
	
	return dp[x][y] = get(x-1, y) + get(x, y-1);
}

int get2(int x, int y, int limitx, int limity) {
	if (x < limitx || y < limity) return 0;
	if (dp[x][y]) return dp[x][y];
	
	return dp[x][y] = get2(x-1, y, limitx, limity) + get2(x, y-1, limitx, limity);
}
int main() {
	cin >> n >> m >> k;
	
	if (!k) {
		cout << get(n-1, m-1) << endl;
	} else {
		int xpos = (k-1) / m;
		int ypos = (k-1) % m;
		get(xpos, ypos);
		cout << get2(n-1, m-1, xpos, ypos) << endl;
	}
}


4번 버스 노선 문제입니다. (백준 10165)

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


케이스를 잘 나누어야 한다. 일단 노선이 직선일 경우에 대하여 생각을 해 보자. 비교 연산자를 재정의해서 소팅해서, 시작위치는 오름차순으로, 끝 위치는 내림차순으로 정렬한다.    

그러면 소팅을 하고 나서 선형탐색을 앞에서 뒤로 할 때, 뒤에 나타난 녀석이 앞에 있는 녀석을 포함하는 경우는 없다. 

시작위치는 뒤로 갈 수록 앞으로 가기 때문에 시작 범위는 줄어들기 마련이다.    

그리고 시작 위치가 고정인 상태에서는 끝나는 위치는 계속해서 작아지게 되어있으므로, 시작 위치가 다음 값이 되었을 때 끝 위치가 가장 큰놈 일 것이다. 이러한 점을 이용해서 끝놈 위치만 저장해 놓고, 끝놈보다 작으면 포함시켜버리고, 끝놈보다 크면, 그 끝놈 위치를 새 끝놈 위치로 저장해서 끝만 비교하면서 선형 탐색을 하면 된다.    


그러면 또 노선이 원형일 경우를 체크를 해야 하는데, 시작점<끝점 의 경우 A그룹으로 정의하고 시작점>끝점 의 경우를 B그룹으로 정의를 하자. B그룹의 경우는 0번위치를 무조건 통과하고 A그룹은 0번 위치를 지나갈 수는 없다. 따라서 A그룹이 B그룹을 포함할 수는 없다. 

B그룹에서 시작 부분의 최대값을 구하고, 끝 부분의 최소값을 구해서 A그룹의 (?~시작부분 최대값) 의 경우와 (끝부분 최소값~?)을 모두    

포함할 수 있다. 이러한 경우를 체크해 주고(B가 A를 포함하는 경우) A그룹이 A를 포함하는 경우와 B그룹이 B를 포함하는 경우는 직선에서 비교하는 것과 똑같은 방법으로 체크하면 된다.

#include <bits/stdc++.h> using namespace std; int n, m; struct Route { int src, dst, num; }; bool cmp(const struct Route& lhs, const struct Route& rhs) { if (lhs.src != rhs.src) return lhs.src < rhs.src; return lhs.dst > rhs.dst; } vector<Route> small, large; //인덱스 숫자가 작을수록 범위가 큰놈이다. vector<bool> ans; int main() { cin >> n >> m; ans.reserve(m+1); int large_over = -1; int large_below = n+1; int over = 0; for (int i=1; i<=m; i++) { Route tmp; scanf("%d %d", &tmp.src, &tmp.dst); tmp.num = i; if (tmp.src < tmp.dst) { small.push_back(tmp); } else { large_over = max(large_over, tmp.dst); large_below = min(large_below, tmp.src); large.push_back(tmp); } } sort(small.begin(), small.end(), cmp); sort(large.begin(), large.end(), cmp); //초기 large_over값은 large가 small 덮어쓰는 경우(0~over 범위로 덮는 경우임) //초기 large_below값은 large가 small 덮어쓰는 경우(below~N) 범위로 덮는 경우 //진행하다보면 small이 small 덮어쓰는 경우도 있음 for (size_t i=0; i < small.size(); i++) { if (small[i].dst <= large_over) ans[small[i].num] = true; else if (small[i].src >= large_below) ans[small[i].num] = true; if (small[i].dst <= over) { ans[small[i].num] = true; } else { over = small[i].dst; } } over = -1; //large가 large 덮어쓰는 경우 for (size_t i=0; i < large.size(); i++) { if (large[i].dst <= over) ans[large[i].num] = true; else over = large[i].dst; } for (int i=1; i <= m; i++) { if (!ans[i]) { cout << i << '\n'; } } return 0; }


한국정보올림피아드(정올) 2017년도 초등부 문제 풀이 포스팅입니다.


문제들은 백준저지에서 참고하고 풀이했습니다.

https://www.acmicpc.net/category/detail/1775


총 4문제로 이루어져 있는데, 제가 풀 때 당시에는 백준저지에는 3문제만 올라와있어서 3문제에 대한 코드만 올립니다.


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


2번, 방 배정하기 문제입니다. (백준저지 14697)


N값이 작으므로 3중 포문으로 O(N^3)으로 완전탐색으로 Naive하게 풀었습니다.


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

int main() {
    int a, b, c, total;
    cin >> a >> b >> c >> total;
    
    int a_lim = total/a;
    int b_lim = total/b;
    int c_lim = total/c;
    for (int i=0; i <= c_lim; i++) {
        int cur = c * i;
        for (int j=0; j <= b_lim; j++) {
            int cur2 = cur + j*b;
            for (int k=0; k <= a_lim; k++) {
                int cur3 = cur2 + k*a;
                if (cur3 == total) {
                    cout << 1 << '\n';
                    return 0;
                }
            }
        }
    }
    cout << 0 << '\n';
	return 0;
}


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

3번, 서울에서 경산까지 문제입니다. (백준저지 14863)


DFS를 통한 완전탐색으로 풀되, 중복되어 풀리는 문제들(Overlapping-subproblems)는 Memoization 패턴을 이용하여 중복해서 풀리지 않도록 처리했습니다. 그리고 마지막 구간까지 도달하지 못하는 경우에 대해서 제외하도록 처리해야하는 것이 좀 까다로웠습니다.



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

#define IMPOSSIBLE -987654321
typedef long long ll;
int dp[101][100001]; //[range][minute left]
int walk_min[101];
int walk_get[101];
int bicy_min[101];
int bicy_get[101];
int n, k;
int get(int segment, int minute_left) {
    if (segment >= n) return 0;
    if (minute_left <= 0) return IMPOSSIBLE;
    int& ret = dp[segment][minute_left];
    if (ret != -1) {
        return ret;
    };
    
    ret = IMPOSSIBLE;
    if (minute_left >= walk_min[segment] and get(segment+1, minute_left - walk_min[segment]) != IMPOSSIBLE) {
        ret = max(ret, get(segment+1, minute_left - walk_min[segment]) + walk_get[segment]);
    }
    if (minute_left >= bicy_min[segment] and get(segment+1, minute_left - bicy_min[segment]) != IMPOSSIBLE) {
        ret = max(ret, get(segment+1, minute_left - bicy_min[segment]) + bicy_get[segment]);
    }
    return ret;
    
}

int main() {

    scanf("%d %d", &n, &k);
    for (int i=0; i < n; i++) {
        scanf("%d %d %d %d", &walk_min[i], &walk_get[i], &bicy_min[i], &bicy_get[i]);    
    }
    memset(dp, -1, sizeof(dp));
    
    cout << get(0, k) << '\n';
	return 0;
}


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

4번, 줄서기 문제입니다. (백준저지 14864)


개인적으로 난이도가 꽤나 있었고, 완전 Nice한 해답은 아니라고 생각합니다.

일단 순서쌍에서 알아낼 수 있는 정보는 다음과 같습니다.

1. 나보다 뒤에 있는 놈에 대해서, 내가 그 놈보다 큰지 아닌지를 확인할 수 있다.

2. 나보다 앞에 있는 놈 중에 나보다 큰 놈이 몇 개인지 알 수 있다.    


물론 나보다 앞에 있는 놈 중에 큰놈이 몇개인지 뿐만 아니라 누군지까지 확인할 수 있지만, 일단은 저 두개를 이용해서 문제를 풀어보았습니다.

맨 뒤에서 부터 채워가면서 숫자를 넣을 것인데, 일단 집합 S를 다음과 같이 초기화 합니다. S = {1, 2, 3, ... , N}

맨 뒤에있는 숫자 입장에서는 나머지 모두가 나보다 앞에 있으므로, 나머지 수 중 자신보다 큰 수가 몇개인지 알 수 있습니다.

그러면 앞에있는 수 중 자신보다 큰 수의 개수를 k라고 하면, 집합 S에서 k번째 큰 수를 빼서 자신의 값에 집어넣습니다.


이런식으로 맨 앞까지 진행합니다.


이러면 일단 조건을 만족할만한 정답 셋을 하나 구할 수 있는데, Valid한 정답을 갖는다면 Unique한 정답을 가질 것입니다.

하지만 해당 인풋이 Invalid할 수도 있으니 한번 체크를 해야 합니다. (Invalid한 정답이라면 -1를 출력해야함)

이때 Strict하게 체크를 하려면 자신보다 뒤에 있는 숫자 중, 순서쌍에 해당된다면 자신보다 작은지, 순서쌍에 해당되지 않는다면 자신보다 큰지를 확인해야하는데, 둘다 체크하게 되면 TLE가 날 것입니다.   

특히나 순서쌍애 해당되지 않는 애들까지 체크를 하게 되면 O(N^2)이므로 꼼짝없이 TLE다. 하지만 순서쌍 개수는 100만개이므로, 순서쌍에 해당되는 경우만 체크할 경우 TLE는 안날 수 있습니다. 따라서 순서쌍에 해당되는 경우만 자신보다 작은 값을 갖는지만 체크하면 됩니다.


이 부분의 경우 -1이 정답인데 아니게 나올까봐 조마조마했는데 정답 판정이 났다. 아마 앞에서 만든 Answer Array가 주어진 순서쌍 조건이 맞는지만 확인해도빠진 조건이 있는 반례가 없다는게 논리적으로 맞아서 그런 것인지는 잘 모르겠습니다.

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

#define NUM_STUDENT 100001 //학생수 최대
#define UNDEFINED 0 // 정답이 결정되지 않은 상태
set<int> larger_than_me[NUM_STUDENT], smaller_than_me[NUM_STUDENT];

int ans[NUM_STUDENT];
int n, m;
set<int> rest;
void fail() {
    cout << -1 << '\n';
    exit(EXIT_SUCCESS);
}
int main() {
    scanf("%d%d", &n, &m);
    
    //initialize rest set
    for (int i=1; i<=n;i++) {
        rest.insert(-i);
    }
    //initialize rest set
    
    if (n == 1) { //학생수 1명일때만 예외처리
        cout << 1 << '\n';
        return 0;
    }
    
    for (int i=0; i < m; i++) {
        int a, b;
        scanf("%d %d", &a, &b); // a > b라는 뜻
        smaller_than_me[a].insert(b); // a보다 작은 set에 b 추가.
        larger_than_me[b].insert(a);// b보다 큰 set에 a추가.
    }
    
    for (int cur = n; cur >= 1; cur--) {
        int larger_num = larger_than_me[cur].size();
        auto it = rest.begin();
        for (int i=0; i < larger_num; i++) {
            it++;
        }
        int kth_largest = -*it;
        ans[cur] = kth_largest;
        rest.erase(it);    
    }
    
    //check for validation - 주어진 조건은 맞는지 확인. 빠진 조건이 있는지는 안확인 ㅎㅎㅎ
    for (int cur = 1; cur < n; cur++) {
        int my_val = ans[cur];
        for (auto it : smaller_than_me[cur]) {
            int smaller_val = ans[it];
            if (smaller_val > my_val) {
                fail();
            }
        }
    }
    
    for (int i=1; i <= n; i++) {
        cout << ans[i] << ' ';
    }
    cout << '\n';
	return 0;
}


+ Recent posts