본문 바로가기

알고리즘

Codeforces Round #661 (Div.3)

A. Remove Smallest

https://codeforces.com/contest/1399/problem/A

 

인자가 n개인 배열을 정렬했을 때 계단수인지 확인한다.

정렬된 인자 사이의 차가 1보다 크면 계단수가 아니다.

 

시간 복잡도는 O(NlogN)이다.

 

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstdlib>
using namespace std;
int t, N;
int arr[51];
int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--) {
		cin >> N;
		bool flag = 1;
		for (int i = 0; i < N; i++)cin >> arr[i];
		sort(arr, arr + N);
		for (int i = 0; i < N - 1; i++) {
			if (abs(arr[i] - arr[i + 1]) <= 1)continue;
			else {
				cout << "NO\n";
				flag = 0;
				break;
			}
		}
		if(flag==1)cout << "YES\n";
	}
}

 

B. Gifts Fixing

https://codeforces.com/contest/1399/problem/B

 

크기가 N인 배열이 2개 주어진다(각각 사탕, 오렌지의 개수).

우리는 3개의 연산을 이용해 각각의 배열의 값을 같게 만들어야 한다.

쓸 수 있는 연산은 다음과 같다.

1. 사탕을 1개 먹는다.

2. 오렌지를 1개 먹는다.

3. 사탕과 오렌지를 1개씩 먹는다.

 

그러므로, 우리는 먼저 사탕과 오렌지 배열의 최솟값을 찾고, 최솟값까지 몇 개를 먹어야 되는지 알아야 한다.

 

예를 들어, 사탕의 개수가 { 1, 2, 3 }이고, 오렌지의 개수가 { 5, 2, 4 }라면,

사탕의 개수를 { 1, 1, 1 }, 오렌지의 개수를 { 2, 2, 2 } 로 맞추는 것이 정답이다.

 

각각의 index에 대해 연산의 최솟값은 max(사탕만 먹는 연산의 횟수, 오렌지만 먹는 연산의 횟수)이다.

 

시간 복잡도는 O(N)이다.

 

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstdlib>
using namespace std;
int t, N;
int app[51], ora[51];
long long mn = 2100000000, mn2 = 2100000000;
long long cnt;
int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--) {
		cin >> N; mn = 2100000000;
		mn2 = 2100000000; cnt = 0;
		for (int i = 0; i < N; i++) {
			cin >> app[i];
			mn = min(mn, (long long)app[i]);
		}
		for (int i = 0; i < N; i++) {
			cin >> ora[i];
			mn2 = min(mn2, (long long)ora[i]);
		}
		for (int i = 0; i < N; i++) {
			cnt += max(app[i] - mn, ora[i] - mn2);
		}
		cout << cnt << "\n";
	}
}

 

C. Boats Competition

https://codeforces.com/contest/1399/problem/C

 

크기가 N인 배열을 입력받고, 임의의 정수 s에 대해 두 원소를 합했을 때 s를 만들 수 있는 최댓값 k를 최대화하는 문제이다.

 

예를 들어,

{ 6, 6, 6, 6, 6, 6, 8, 8 }에 대해

s가 될 수 있는 수는 12, 14, 16이다.

 

s = 12일 때, { 6, 6 }, { 6, 6 }, { 6, 6 } 으로 s를 만들 수 있어 k = 3이다.

s = 14일 때, { 6, 8 }, { 6, 8 } 인 경우가 가능해 k = 2이다.

s = 16일 때는 { 8, 8 } 만 가능해 k = 1이다.

답은 k의 값들 중 최댓값인 3이다.

 

여기서, 우리는 s가 주어질 때의 k 값을 모두 구해놓으면 k의 최댓값이 답이 될 수 있다는 것을 알 수 있다.

 

문제의 조건에 따라 원소에 들어갈 수 있는 수는 원소의 크기 N(<=50)보다 작거나 같다.

따라서, 2 <= s <= 100이다.

s가 2~100일 때 가질 수 있는 순서쌍의 개수를 map에 저장한다.

map[s] = k의 형식으로 저장한다.

 

그 뒤, map을 순회하며 저장된 k 중의 최댓값을 구한다.

실제 소스 코드에서는 k가 최대가 되는 s값에 대해 k를 다시 구해줬다.

 

시간 복잡도는 O(N^2)이다.

 

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <map>
using namespace std;
int t, N;
int w[51];
int mx, mxnum, ans;
bool used[51];
map<int, int> m;//sum, cnt
int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--) {
		cin >> N;
		for (int i = 1; i <= N; i++) {
			cin >> w[i];
			used[i] = 0;
		}
		m.clear();
		for (int s = 2; s <= 100; s++) {
			for (int i = 1; i < s; i++) {
				if (count(w+1, w + N+1, i)) {
					m[s] += min(count(w + 1, w + N + 1, i), count(w + 1, w + N + 1, s - i));
				}
			}
		}
		/*
		for (int i = 1; i <= N; i++) {
			for (int j = i; j <= N; j++) {
				if (i == j)continue;
				m[w[i] + w[j]]++;
			}
		}
		*/
		mx = 0; mxnum = 0; ans = 0;
		for (auto i : m) {
			if (mx < i.second) {
				mxnum = i.first;
			}
			mx = max(mx, i.second);
		}
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				if (used[i] || used[j])continue;
				if (i == j)continue;
				if (w[i] + w[j] == mxnum) {
					used[i] = used[j] = 1;
					ans++;
				}
			}
		}
		cout << ans << "\n";
	}
}

 

D. Binary String To Subsequences

https://codeforces.com/contest/1399/problem/D

 

문제 이해가 어려웠는데, 문제를 조금 풀어서 설명하면,

 

1. 길이가 N인 0과 1로 이루어진 문자열 s이 주어진다.

2. 이 문자열의 subsequence는 이 문자열의 임의의 인덱스를 삭제시킨 문자열로 정의한다.

임의의 인덱스를 몇개든 없앨 수 있지만, 순서는 바꿀 수 없다. (ex: 1011101에서 11100으로 바꿀 수 없다.)

3. 우리는 s의 각 자리수 a에 대해서 모든 a가 s의 subsequence에 포함되도록 하고 싶다.

4. 이 때, subsequence는 반드시 "101010..." 또는 "010101..."의 형태여야 한다.

5. subsequence의 개수가 가장 적을 때, 각 a가 몇 번째 subsequence인지 출력한다.

 

구현에 앞서, 예제를 살펴보자.

 

s = 10101의 경우, 모든 a가 s의 subsequence = 10101에 포함될 수 있기 때문에

1 1 1 1 1을 출력한다.

 

s = 01010000의 경우, 처음 5개의 a는 subsequence = 01010에 포함될 수 있기 때문에 1의 값을 갖고,

뒤의 3개의 a는 각각 고유한 subsequence = 0에 포함되어야 하므로

1 1 1 1 1 2 3 4를 출력한다.

 

 

구현 방법은 다음과 같다.

 

1. 일단 모든 subsequence의 가장 마지막 자리(마지막 자리가 a가 몇 번째 subsequence에 포함되는지를 결정하기 때문이다)를 저장한다(소스 코드에선 vector<char> last).

 

2. a가 저장된 last[i]^1과 같다면, 기존의 subsequence에 포함될 수 있으므로, 기존의 last[i]에 ^= 1을 해준다.

 

3. 만약 모든 last[i]^1과 다르다면, 기존의 subsequence에 포함될 수 없으므로, 새로운 subsequence를 만들고, subsequence의 마지막 자리를 a로 설정한다.

 

4. 만약 a가 모든 last[i]와 다르고, a의 다음 자리 b 또한 a와 같다면, b도 모든 last[i]와 다름이 자명하다. 그러므로 모든 last[i]와 같은지 체크하는 절차 없이 바로 subsequence를 하나 추가해준다.

 

이것을 naive하게 구현하면 시간복잡도가 O(N^2)인데, 처음에 TLE가 나길래 4번을 추가해서 AC를 받을 수 있었다.

 

2, 3번 과정에서 lower_bound를 이용하면 총 시간복잡도를 O(NlogN)으로 줄일 수 있다.

 

문제 푸는 동안에 binary search를 쓸 생각을 하지 못했다. 앞으로 더 열심히 해야겠다.

 

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <map>
#include <cstring>
using namespace std;
int t, N;
string str;
vector<char> last;
int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--) {
		cin >> N;
		cin >> str;
		last.clear();
		bool good = 0, lastgood = 0;
		string ans = "";
		for (int i = 0; i < N; i++) {
			if (i != 0 && good == 0 && str[i] == str[i - 1]) goto lol;
			good = 0;
			for (int j = 0; j < last.size(); j++) {
				if ((str[i] - '0') ^ 1 == last[j] - '0') {
					last[j] ^= 1;
					good = 1;
					ans += to_string(j + 1);
					ans += ' ';
					break;
				}
			}
			if (good == 1) continue;
			lol:
			last.push_back(str[i]);
			ans += to_string(last.size());
			ans += ' ';
		}
		cout << last.size() << '\n';
		cout << ans << '\n';
	}
}

'알고리즘' 카테고리의 다른 글

Codeforces Round #686 (Div.3)  (0) 2020.11.25
Codeforces Round #685 (Div.2)  (0) 2020.11.24
Educational Codeforces Round 98 (Div.2)  (0) 2020.11.21
Codeforces Round #684 (Div.2)  (0) 2020.11.18
Codeforces Round #663 (Div.2)  (0) 2020.08.10