PS/백준

[백준] 1017 소수 쌍 (C++)

dong_gas 2022. 2. 10. 04:17

오늘 학교에서 풀어서 맘에 드는 문제.

 

1017번: 소수 쌍

지민이는 수의 리스트가 있을 때, 이를 짝지어 각 쌍의 합이 소수가 되게 하려고 한다. 예를 들어, {1, 4, 7, 10, 11, 12}가 있다고 하자. 지민이는 다음과 같이 짝지을 수 있다. 1 + 4 = 5, 7 + 10 = 17, 11 +

www.acmicpc.net

 

1.

크기가 짝수인 배열이 주어진다. (배열의 원소는 서로 다르다.)

남는 거 없이 2개씩 짝지어야 한다. 각 짝의 합은 소수가 되어야 한다.

 

2.

어떻게 두 개를 짝지을 수 있을까?

서로 다른 두 수를 더해서 소수가 되는 경우를 살펴보자.

- 짝수끼리 더하면 무조건 짝수니까 소수가 나올 수 없다.

- 홀수끼리 더하면 무조건 짝수니까 소수가 나올 수 없다. (1+1은 소수지만 배열의 원소는 서로 다르다고 했다.)

 

3.

짝수와 홀수를 짝지어야 하니까 홀짝을 기준으로 이분그래프를 모델링하여 합이 소수가 되게 이분매칭을 하자!

이때, (배열의 짝수의 개수 == 배열 내의 홀수의 개수)여야함은 쉽게 알 수 있다. 아니라면 -1 출력하자.

 

4.

이분매칭한 개수가 (배열의 크기/2)와 같지 않으면 조건에 부합하지 않으므로 -1을 출력하자.

 

5.

여기서 끝이 아니다.

가능한 모든 매칭을 찾아야 한다. 모든 매칭마다 배열의 첫 원소가 매칭 된 녀석을 출력해야 하기 때문이다.

 

6.

그러면 이제 또 다른 매칭 방법이 있는지는 어케 찾을까?

 

이분매칭의 논리를 이용하자.

- 일단 첫 원소의 매칭을 끊어준다.

- 첫 원소와 매칭이 가능한 놈이 있으면 매칭 시켜준다.

이를 가능한 모든 경우에 대해 반복하면서 답이 되는 값들을 찾는다.

 

 

 

구현이 쉽지 않다. (내가 어렵게 해서ㅋㅋ)

 

코드

더보기
#include <bits/stdc++.h>
#define endl '\n'
#define MOD 998244353
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;

ll n;
ll arr[51];
ll sieve[2001];
ll aMatch[1001];
ll bMatch[1001];
ll visited[1001];
vector<ll> even;
vector<ll> odd;
vector<ll> ans;

bool dfs(ll a) {
	if (visited[a]) return false;
	visited[a] = 1;
	for (ll b = 0; b < even.size(); b++) {
		if (!sieve[a + even[b]]) continue;
		if (bMatch[even[b]] == -1 || dfs(bMatch[even[b]])) {
			aMatch[a] = even[b];
			bMatch[even[b]] = a;
			return true;
		}
	}
	return false;
}

ll bipartiteMatch() {
	ll size = 0;
	for (ll from = 0; from < odd.size(); from++) {
		memset(visited, 0, sizeof(visited));
		if (dfs(odd[from])) size++;
	}
	return size;
}

void solve() {
	memset(aMatch, -1, sizeof(aMatch));
	memset(bMatch, -1, sizeof(bMatch));
	cin >> n;
	for (ll i = 1; i <= n; i++) {
		cin >> arr[i];
		if (arr[i] % 2) odd.push_back(arr[i]);
		else even.push_back(arr[i]);
	}
	if (arr[1] % 2 == 0) swap(odd, even);
	if (odd.size() != even.size()) {
		cout << -1 << endl;
		return;
	}

	if (bipartiteMatch() != n / 2) {
		cout << -1 << endl;
		return;
	}

	for (ll i = 0; i < even.size(); i++) {
		if (sieve[odd[0] + even[i]]) {
			ll aa = aMatch[odd[0]];
			ll bb = bMatch[aMatch[odd[0]]];
			bMatch[aMatch[odd[0]]] = -1;
			aMatch[odd[0]] = -1;
			memset(visited, 0, sizeof(visited));
			if (bMatch[even[i]] == -1 || dfs(bMatch[even[i]])) {
				aMatch[odd[0]] = even[i];
				bMatch[even[i]] = odd[0];
				ans.push_back(even[i]);
			}
			else {
				aMatch[odd[0]] = aa;
				bMatch[aMatch[odd[0]]] = bb;
			}
		}
	}
	sort(ans.begin(), ans.end());
	for (ll i = 0; i < ans.size(); i++) cout << ans[i] << ' ';
}

int main() {
	ios::sync_with_stdio(0); cin.tie(0);
	//에라토스테네스의 체, 1이면 소수
	memset(sieve, 1, sizeof(sieve));
	sieve[1] = 0;
	for (ll i = 2; i <= 2000; i++) {
		for (ll j = 2; i * j <= 2000; j++) {
			sieve[i * j] = 0;
		}
	}
	int tc = 1; //cin >> tc;
	while (tc--) solve();
	return 0;
}

'PS > 백준' 카테고리의 다른 글

[백준] 2325 개코전쟁, 2307 도로검문 (C++)  (4) 2022.02.13
[백준] 1671 상어의 저녁식사 (C++)  (0) 2022.02.12
[백준] 10948 Daily 로또 (Text)  (2) 2021.12.14
[백준] 23560 약 (C++)  (0) 2021.11.16
[백준] 1563 개근상 (C++)  (0) 2021.11.06