처음 이 문제를 접했을 때에는 '혹시 쌍둥이 소수' 문제일까 하고 봤었네요. 소수쌍이 아니라 합이 소수가 되는 숫자들의 합 이야기입니다. 짝수인 N개가 주어지면, 합이 소수가 되는 쌍으로 모두 묶을 수 있다면, 첫번째 수와 묶일 수 있는 수들의 리스트를 내는 문제입니다. 난이도는 Platinum III이지만, 난이도보다는 쉬운 문제입니다. 물론 네트워크 플로우 알고리즘을 알고 있을 때에 한합니다. DFS 알고리즘으로도 풀 수 있지만, 그렇게 하면 시간초과가 납니다.
아래는 해당 문제의 링크입니다.
https://www.acmicpc.net/problem/1017
1017번: 소수 쌍
지민이는 수의 리스트가 있을 때, 이를 짝지어 각 쌍의 합이 소수가 되게 하려고 한다. 예를 들어, {1, 4, 7, 10, 11, 12}가 있다고 하자. 지민이는 다음과 같이 그룹지을 수 있다. 1 + 4 = 5, 7 + 10 = 17, 11 + 12 = 23 또는 1 + 10 = 11, 4 + 7 = 11, 11 + 12 = 23 수의 리스트가 주어졌을 때, 지민이가 모든 수를 다 짝지었을 때, 첫 번째 수와 어떤 수를 짝지었는지 오름차순으로 출력하는
www.acmicpc.net
이 문제에서는 소수를 빨리 찾는 방법이 관건입니다. 리스트에 있는 수들의 50개이므로, 첫번째수와 연결되는 두번째 수를 선택하고 나머지에 대해서 소수쌍이 되는지 검사하면 됩니다. 미리 매칭 테이블을 만들어놓아서 한다면, 최대 \(25 \times 49=1225\)번의 소수 검사만 하면 됩니다. 나올 수 있는 소수 크기가 2,000 미만이므로 소수 검사비용도 상당히 저렴합니다.
알고리즘은 간단합니다.
첫번째 수는 당연히 필요한 것이고, 두번째 수를 차례대로 선택한 후, 이 합이 소수이면, 나머지 수들에 대해서는 네트워크플로우를 이용해서 소수쌍으로 모든 수들이 참여가능한지 판단합니다. 그렇게 판단이 되면 두번째 수를 출력하고 이 과정을 반복하면 됩니다.
제가 작성한 소스입니다.
//----------------------------------------------------------
// baekjoon #1017 - Prime Pairs
// - by Aubrey Choi
// - created at 2019-05-20
//----------------------------------------------------------
#include <stdio.h>
#include <memory.h>
static unsigned char primes[] = {
0xac,0x28,0x8a,0xa0,0x20,0x8a,0x20,0x28, 0x88,0x82,0x08,0x02,0xa2,0x28,0x02,0x80,
0x08,0x0a,0xa0,0x20,0x88,0x20,0x28,0x80, 0xa2,0x00,0x08,0x80,0x28,0x82,0x02,0x08,
0x82,0xa0,0x20,0x0a,0x20,0x00,0x88,0x22, 0x00,0x08,0x02,0x28,0x82,0x80,0x20,0x88,
0x20,0x20,0x02,0x02,0x28,0x80,0x82,0x08, 0x02,0xa2,0x08,0x80,0x80,0x08,0x88,0x20,
0x00,0x0a,0x00,0x20,0x08,0x20,0x08,0x0a, 0x02,0x08,0x82,0x82,0x20,0x0a,0x80,0x00,
0x8a,0x20,0x28,0x00,0x22,0x08,0x08,0x20, 0x20,0x80,0x80,0x20,0x88,0x80,0x20,0x02,
0x22,0x00,0x08,0x20,0x00,0x0a,0xa0,0x28, 0x80,0x00,0x20,0x8a,0x00,0x20,0x8a,0x00,
0x00,0x88,0x80,0x00,0x02,0x22,0x08,0x02, 0x80,0x08,0x82,0x80,0x20,0x00,0x22,0x28,
0x80,0x82,0x00,0x0a,0xa0,0x20,0x00,0x80, 0x28,0x82,0x20,0x20,0x08,0x02,0x00,0x80,
0x02,0x08,0x08,0x20,0x08,0x02,0x02,0x20, 0x82,0xa0,0x20,0x00,0x02,0x08,0x00,0xa0,
0x08,0x0a,0xa2,0x08,0x80,0x82,0x00,0x00, 0x00,0x00,0x82,0x20,0x20,0x00,0x80,0x00,
0x02,0x80,0x28,0x82,0x80,0x28,0x08,0x80, 0x00,0x8a,0x22,0x08,0x80,0x00,0x08,0x08,
0x80,0x20,0x82,0x80,0x08,0x88,0x00,0x20, 0x82,0x22,0x28,0x08,0x20,0x00,0x00,0x82,
0x28,0x00,0x00,0x20,0x0a,0x20,0x00,0x0a, 0x20,0x20,0x08,0x82,0x00,0x00,0x82,0x28,
0x00,0x02,0x08,0x80,0x80,0x00,0x80,0x00, 0x20,0x88,0xa2,0x00,0x02,0x20,0x08,0x02,
0x00,0x28,0x00,0xa0,0x00,0x00,0x20,0x08, 0x08,0xa2
};
inline bool isprime(int p) { return (bool)((primes[p / 8] >> (p % 8)) & 1); }
char b[25], v[25], adj[25][26]; int n;
bool dfs(int s)
{
v[s] = 1;
for(int i = 1; i <= adj[s][0]; i++)
{
int t = adj[s][i];
if(b[t] == -1 || (v[b[t]] == 0 && dfs(b[t])))
{
b[t] = s;
return true;
}
}
return false;
}
bool checkmatch(int s, int n)
{
memset(b, 0xff, sizeof(b));
b[s] = 0;
for(int i = 1; i < n; i++)
{
memset(v, 0, sizeof(v));
v[0] = 1;
if(!dfs(i)) return false;
}
return true;
}
int main()
{
int l[25], r[25], lcount = 1, rcount = 0, t, i, j;
scanf("%d%d",&n,&l[0]);
for(i=1;i<n;i++) { scanf("%d",&t); if((t+l[0])%2) r[rcount++]=t; else l[lcount++]=t; }
if(lcount != rcount) { puts("-1"); return 0; }
// check primes between elements.
for(i = 0; i < lcount; i++)
{
int count = 0;
for(j = 0; j < lcount; j++)
{
if(isprime(l[i] + r[j])) adj[i][++count] = j;
}
adj[i][0] = count;
if(!count) { puts("-1"); return 0; }
}
int ans[100], count = 0;
for(i=1;i<=adj[0][0];i++)
{
int s = adj[0][i];
if(checkmatch(s, lcount))
{
int last = count++;
while(last > 0 && ans[last-1] > r[s])
{
ans[last] = ans[last-1];
last--;
}
ans[last] = r[s];
}
}
if(count == 0) { puts("-1"); return 0; }
for(i = 0; i < count; i++) printf("%d ", ans[i]); putchar('\n');
}
'Programming > BOJ' 카테고리의 다른 글
백준 #1019 책 페이지 (0) | 2019.12.25 |
---|---|
[C/C++] 백준 #1018 체스판 다시 칠하기 (0) | 2019.12.24 |
백준 #1016 제곱 ㄴㄴ수 (0) | 2019.12.24 |
[C/C++] 백준 #1015 수열 정렬 (0) | 2019.12.23 |
[C/C++] 백준 #1012 유기농 배추 (0) | 2019.12.22 |
댓글