문제 : https://www.acmicpc.net/problem/16288


[알고리즘풀이]

A[] : 입국장을 빠져나가는 순서를 담은 배열.

k : 여권 심사 창구의 수.

C : A배열에서 순증가부분수열의 수.

라고 한다면 k 가 C보다 크거나 같으면 항상 원하는 순서대로 입국장을 빠져나갈 수 있다.

 

ex)

A[] : 4 1 3 2 5 6 8 9 7 10 라고 하자.

A배열의 순증가부분수열은 4 - 5 - 6 - 8 - 9 - 10 / 1 - 3 - 7 / 2 로 총 3개이다.

즉, 창구가 3개 이상이면 각 창구마다 위의 수열대로 배치를 할 수 있다.

#include<iostream>

using namespace std;

bool check[101];
int A[100];

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int n, k;
	cin >> n >> k;
	for (int i = 0; i < n; i++)
		cin >> A[i];
	int c = 0;
	for (int i = 0; i < n; i++) {
		if (check[A[i]] == false) {
			c++;
			int last = A[i];
			for (int j = i; j < n; j++) {
				if (last <= A[j] && check[A[j]] == false) {
					last = A[j];
					check[A[j]] = true;
				}
			}
		}
	}
	if (c <= k)
		cout << "YES\n";
	else
		cout << "NO\n";
	return 0;
}

 

'Problem Solving > ICPC' 카테고리의 다른 글

[ICPC][BOJ] 16287 - Parcel  (0) 2019.09.28
[ICPC][BOJ] 16282 - Black Chain  (0) 2019.09.28
[ICPC][BOJ] 16283 - Farm  (0) 2019.09.28
[ICPC][BOJ] 13328 - Message Passing  (0) 2019.09.23
[ICPC][BOJ] 13333 - 'Q-Index'  (0) 2019.09.23

문제 : https://www.acmicpc.net/problem/16287


[알고리즘풀이]

1. N개의 집합에서 서로 다른 2개의 소포를 뽑는 모든 경우에 대해 무게의 합을 따로 Sum 배열에 저장한다.

또, Sum 배열에 몇 번 / 몇번 소포를 합했는지 Index를 저장한다.

2. Binary Search를 위해 Sum 배열을 Sorting 한다.

3. Sum 배열을 순회하며, Binary Search를 이용해 합이 W가 되는 경우를 다른 Sum 배열의 원소를 찾고,

이때, 저장된 Index를 확인해 W가 되는 2개의 Sum 원소가 서로 다른 4개의 소포가 합해진 경우면 "YES"를 출력하고

배열을 다 순회해도 합이 W가 되고 Index가 다 다른 경우를 못 찾는다면 "NO"를 출력한다.

 

[상세풀이]

단순하게 생각하면 N개의 집합에서 서로 다른 4개의 소포를 4중 for문을 이용해서 모두 뽑아서 확인하는 것이다.

하지만, 이러면 O(N^4)로 당연히 시간 초과 문제에 걸린다.

이 문제는 N이 최대 5000 이므로, 2중 for문은 시간 초과에 걸리지 않는다.

따라서, N개의 집합에서 서로 다른 2개의 소포를 뽑는 모든 경우에 대해 무게의 합을 Sum 배열에 저장하고,

이 S배열에서 2개를 뽑는 방법을 사용할 것이다.

( S 배열의 크기는 N개에서 서로 다른 2개를 순서 상관없이 뽑는 경우의 수로 (N * (N-1)) / 2가 된다. )

int A[5000];
pair<int, int> sum[12497500]; // 5000 * 4999 / 2

ex) A[5] = { 1, 2, 3, 4, 5 } 라고 하자.

그러면 S[10] = { 1 + 2, 1 + 3, 1 + 4, 1 + 5, 2 + 3, 2 + 4, 2 + 5, 3 + 4, 3 + 5, 4 + 5}를 담는다.

 

이때, W = 10이라고 하면, S[0] = 3 과 S[6] = 7을 합치면 10이 된다.

하지만 S[0]은 1,2 번 소포를 합친 것이고 S[6]은 2,5번 소포를 합친 것이므로 2번 소포를 두 번 인용하게 된다.

따라서 S배열에 어떤 소포를 합친 건지 Index도 저장해야 된다.

Index는 N이 최대 5000이므로 0 ~ 4999번 중에 하나고 최대 4자리 수이므로 다음과 같은 기법을 통해 저장할 수 있다.

	int p = 0;
	for (int i = 0; i < n; i++) {
		for (int j = i + 1; j < n; j++) {
			sum[p].second =  i * 10000 + j;
			sum[p++].first = A[i] + A[j];
		}
	}

sum[p].second에 'i 와 j번 째 소포를 합쳤습니다.'라는 정보를 담아줄 것입니다.

이때, i * 10000 + j 를 sum[p].second에 저장한다면 10000 이상의 자리는 i 를 10000 미만의 자리는 j를 나타냅니다.

ex) 1번, 4999번 소포를 합쳤다고 한다면 sum[p].second = 14999 가 되고 1 / 4999로 해석할 수 있다.

 

그 후, Sum 배열을 모두 순회하며 합쳤을 때, W가 되는 짝을 구해야 되는데,

Linear Search로는 P개의 Sum 배열에 대해 W가 되는 짝을 배열을 모두 순회하며 찾아야 되므로 O(P^2)이 된다.

Sum 배열은 N*(N-1) / 2 개의 원소를 가지고 있으므로 결과적으로 O(N^4)이 되므로

Binary Search를 이용해야 된다. 따라서, Sum 배열을 먼저 Sorting 해줘야 한다.

bool cmp(pair<int, int> a, pair<int, int> b) {
	if (a.first < b.first)
		return true;
	else
		return false;
}

sort(sum, sum + p, cmp);

 그 후 Binary Search를 이용하고 Index를 확인해서 서로 다 다른 4개의 소포의 합이 W가 되는 경우를 찾아준다.

#include<iostream>
#include<algorithm>

using namespace std;

int A[5000];
pair<int, int> sum[12497500];

bool cmp(pair<int, int> a, pair<int, int> b) {
	if (a.first < b.first)
		return true;
	else
		return false;
}
int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n, w;
	cin >> w >> n;
	for (int i = 0; i < n; i++) {
		cin >> A[i];
	}
	int p = 0;
	for (int i = 0; i < n; i++) {
		for (int j = i + 1; j < n; j++) {
			sum[p].second =  i * 10000 + j;
			sum[p++].first = A[i] + A[j];
		}
	}
	sort(sum, sum + p, cmp);

	for (int i = 0; i < p; i++) {
		int left = 0, right = p - 1;
		while (left <= right) {
			int mid = (left + right) / 2;
			if (sum[i].first + sum[mid].first == w) {
				int index[4] = { sum[i].second / 10000, sum[i].second % 10000, sum[mid].second / 10000, sum[mid].second % 10000 };
				bool flag = false;
				for (int j = 0; j < 4; j++) {
					for (int k = j + 1; k < 4; k++) {
						if(index[j] == index[k]) {
							flag = true;
							break;
						}
					}
					if (flag)
						break;
				}
				if (flag) {
					break;
				}
				else {
					cout << "YES\n";
					return 0;
				}
			}
			else if (sum[i].first + sum[mid].first < w)
				left = mid + 1;
			else
				right = mid - 1;
		}
	}
	cout << "NO\n";
	return 0;
}

 

'Problem Solving > ICPC' 카테고리의 다른 글

[ICPC][BOJ] 16288 - Passport Control  (0) 2019.09.28
[ICPC][BOJ] 16282 - Black Chain  (0) 2019.09.28
[ICPC][BOJ] 16283 - Farm  (0) 2019.09.28
[ICPC][BOJ] 13328 - Message Passing  (0) 2019.09.23
[ICPC][BOJ] 13333 - 'Q-Index'  (0) 2019.09.23

문제 : https://www.acmicpc.net/problem/16282


[알고리즘 풀이]

이 문제의 핵심은 체인을 빼내면 항상 1g짜리 체인이 생기고, 체인이 1을 기준으로 왼쪽 / 1 / 오른쪽으로 3등분으로

나뉜다. 즉, 우리는 체인을 한 번 빼내면 다음과 같은 3개의 체인을 얻을 수 있다. K(g) / 1(g) / L(g)

 

우리는 체인을 빼내는 횟수를 최소화하고 싶으므로, i번 체인을 뺐을 때 최대 몇 g까지 표현이 가능한지를 생각해보자.

 

-체인을 1번 뺌-

K1 / 1 / K2 최대 3등분으로 나눠지고, 2가 없으므로 2가 필요하고, 2 / 1 이 있다고 가정하면 4가 필요하다.

즉, 최대 1번 체인을 빼서 1 + 2 + 4 = 7g 까지 표현이 가능하다.

-체인을 2번 뺌-

K1 / 1 / K2 / 1 / K3 최대 5등분으로 나눠지고, 1이 2개 있으므로 3이 필요하고, 1 + 1 + 3 = 5 이므로 6이 필요하고,

1 + 1 + 3 + 6 = 11이므로 12가 필요하다.

즉, 최대 2번 체인을 빼서 1 + 1 + 3 + 6 + 12 = 23g 까지 표현이 가능하다.

-체인을 i번 뺌-

i번 체인을 빼내면 1g짜리 체인이 i개 생긴 것이고, 최대 2 * i + 1개의 체인으로 나눠진다.

그러면 1g까지라 i개 있으므로, (i + 1) g짜리 체인이 필요하고,

그다음엔 1 * i + (i + 1) g까지는 표현이 가능하므로, (2 * i + 1) g짜리 체인이 필요하다.

이 과정을 반복하면 i번 체인을 빼냈을 때, 최대 2^(i + 1) * i + 2^(i+1) - 1 g까지 표현이 가능하다.

 

따라서, 체인을 빼내는 횟수를 늘려가며 최대 표현 가능한 g보다 n이 작아지는 순간 출력해주면 된다.

#include<iostream>

using namespace std;

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	long long n;
	cin >> n;
	for (long long i = 1; ; i++) {
		long long total = i;
		for (int j = 0; j <= i; j++)
			total = total + (total + 1);
		if (n <= total){
			cout << i;
			return 0;
		}
	}
}

 

'Problem Solving > ICPC' 카테고리의 다른 글

[ICPC][BOJ] 16288 - Passport Control  (0) 2019.09.28
[ICPC][BOJ] 16287 - Parcel  (0) 2019.09.28
[ICPC][BOJ] 16283 - Farm  (0) 2019.09.28
[ICPC][BOJ] 13328 - Message Passing  (0) 2019.09.23
[ICPC][BOJ] 13333 - 'Q-Index'  (0) 2019.09.23

문제 : https://www.acmicpc.net/problem/16283


[알고리즘풀이]

단순 구현 문제로, N의 최대 크기가 1000이므로 전체 경우를 다 순회하며 O(N)으로 풀면 된다.

양이 k 마리, 염소가 n - k라고 하고 k를 1부터 n - 1까지 순회하며 가능한 경우를 찾는다.

#include<iostream>

using namespace std;

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

	int a, b, n, w;
	cin >> a >> b >> n >> w;
	int count = 0, index = 0;
	for (int i = 1; i < n; i++) {
		if ((a * i + b * (n - i)) == w) {
			count++;
			index = i;
		}
	}
	if (count == 0 || count > 1)
		cout << -1;
	else
		cout << index << ' ' << n - index;
	return 0;
}

 

'Problem Solving > ICPC' 카테고리의 다른 글

[ICPC][BOJ] 16287 - Parcel  (0) 2019.09.28
[ICPC][BOJ] 16282 - Black Chain  (0) 2019.09.28
[ICPC][BOJ] 13328 - Message Passing  (0) 2019.09.23
[ICPC][BOJ] 13333 - 'Q-Index'  (0) 2019.09.23
[ICPC][BOJ] 13335 - Trucks  (0) 2019.09.23

+ Recent posts