문제

골드바흐의 추측 백준 6588번


리뷰

두 홀수 소수의 합으로 N을 나타낼 수 있는지 검사하는 문제다.

짝수 정수 N의 범위 (6 ≤ N ≤ 1000000)

에라토스테네스의 체 문제 소수 구하기 백준 1929번를 풀어보고 나니까 막막하진 않았다.


문제의 풀이과정은 아래와 같다.

  1. 에라토스테네스의 체로 백만 까지의 소수를 모두 구한다. (Eratos 함수 )
  2. Prime 벡터에 홀수 소수들만 저장한다. (Eratos 함수 )
  3. Prime 벡터를 순회하면서 N-Prime[i] 가 Prime 에 있는지 검사한다. ( Gold 함수 )

Gold 함수 case1

2중 for문으로 작성한 형태

bool Gold(int n){ // Prime 을 순회하면서 n을 a+b로 나타낼수 있는지 확인  

    for(int i = 0; Prime[i] < n; i++){ // a와 b는 n보다는 작을테니 n 직전까지만 탐색

        int target = n - Prime[i]; // n = a + b 가 되어야 하니까 b를 찾아본다. 

        for(int j = i; Prime[j] < n; j++){
            if(target == Prime[j]){
                printf("%d = %d + %d\n", n, Prime[i], Prime[j]);
                return true;
            }
        }
    }

    return false;
}

Gold 함수 case2

for문을 1개로 쓴 형태

bool Gold(int n){

    for(int i = 0; Prime[i] < n; i++){ // a와 b는 n보다는 작을테니 n 직전까지만 탐색

        if(n-Prime[i] == A[n-Prime[i]]){  // n-Prime[i] 값이 소수인지 확인한다 
            printf("%d = %d + %d\n", n, Prime[i], n-Prime[i]);
            return true;
        }
    }

    return false;
}

질문게시판에서 도움 받은 글

Gold 함수의 내부 for문에서 j를 i+1로 초기화 하고 시작했다가 틀렸다.

입력으로 6을 넣었을 때, 6 = 3 + 3 으로 표현할 수 있다. 따라서 내부 for문은 j = i로 초기화 해야 한다.


맞은 코드1

#include <iostream> 
#include <vector>
#include <algorithm>
#define MAX 1000001
using namespace std;

int A[1000001];  // 모든 소수
vector<int> Prime;  // 홀수 소수 

void Eratos(void){ // 백만 까지의 소수를 모두 구한다. 

    A[0] = A[1] = 1; // 0과 1은 소수가 아니다.  

    for(int i = 2; i<= MAX; i++){
        A[i] = i; // A배열 값을 '자기 자신'으로 저장한다.  
    }

    for(int i = 2; i*i <= MAX; i++){

        if(A[i] == i){

            for(int j = i*i; j <= MAX; j+=i){
                A[j] = i;  // 소수의 배수들을 지운다  
            } 
        }
    }

    // 홀수인 소수만 Prime에 저장  
    for(int i = 3; i<= MAX; i++){
        if(A[i] == i) Prime.push_back(A[i]);        
    }
}

bool Gold(int n){ // Prime 을 순회하면서 n을 a+b로 나타낼수 있는지 확인  

    for(int i = 0; Prime[i] < n; i++){

        int target = n - Prime[i];

        for(int j = i; Prime[j] < n; j++){
            if(target == Prime[j]){
                printf("%d = %d + %d\n", n, Prime[i], Prime[j]);
                return true;
            }
        }
    }

    return false;
}

int main(void){

     Eratos();

     while(1){
         int n = 0;
         scanf("%d", &n);

        if(n == 0) break;

         if(!Gold(n)) cout << "Goldbach's conjecture is wrong.\n"<<'\n';
    }

    return 0;    
} 

맞은 코드2

#include <iostream> 
#include <vector>
#include <algorithm>
#define MAX 1000001
using namespace std;

int A[1000001];  // 모든 소수
vector<int> Prime;  // 홀수 소수 

void Eratos(void){

    A[0] = A[1] = 1; // 0과 1은 소수가 아니다  

    for(int i = 2; i<= MAX; i++){
        A[i] = i; // 자기 자신을 저장시킨다  
    }

    for(int i = 2; i*i <= MAX; i++){

        if(A[i] == i){

            for(int j = i*i; j <= MAX; j+=i){  // 소수의 배수들을 지운다  
                A[j] = i; 
            } 
        }
    }

    for(int i = 3; i<= MAX; i++){ // 홀수인 소수만 Prime에 저장  
        if(A[i] == i) Prime.push_back(A[i]);        
    }

}

bool Gold(int n){

    // P 를 순회하면서 n을 a+b로 나타낼수 있는지 확인  
    for(int i = 0; Prime[i] < n; i++){

        if(n-Prime[i] == A[n-Prime[i]]){
            printf("%d = %d + %d\n", n, Prime[i], n-Prime[i]);
            return true;
        }
    }

    return false;
}

int main(void){

     Eratos();

     while(1){
         int n = 0;
         scanf("%d", &n);

        if(n == 0) break;

         if(!Gold(n)) cout << "Goldbach's conjecture is wrong.\n"<<'\n';
    }

    return 0;    
} 
728x90

문제

소수 구하기 백준 1929번

리뷰

M이상 N이하의 소수를 모두 출력하는 프로그램을 작성하시오. 1 ≤ M ≤ N ≤ 1,000,000

에라토스테네스의 체 를 이용한 문제다.

소수의 배수들을 전부 지우는 것이 핵심이다.

2는 소수다. 2의 배수를 전부 지운다.

3은 소수다. 3의 배수를 전부 지운다.

4는 2의 배수이므로 이미 지워졌다. 지나간다.

5는 소수다. 5의 배수를 전부 지운다.

6은 2의 배수이므로 이미 지워졌다. 지나간다.

이것을 코드로 나타내면 다음과 같다.

처음에는 전부 소수라고 0으로 초기화된 배열을 만든다.

그리고 0과 1은 소수가 아니니까 1로 만든다.

int A[1000001] = { 0, };

A[0] = 1; // 0과 1은 소수가 아니다. 지운다. 
A[1] = 1;

반복문 i를 2부터 N까지 순회한다.

앞서 소수 찾기 문제 에서 배운대로 N까지의 소수를 구하려면 루트 N까지만 확인하면 된다.

for(int i = 2; i*i <= N; i++){

    if(A[i] == 0) {  // 소수 라면, 

        for(int j = i*i; j <= N; j+=i ){ // 소수 i의 배수들을 전부 지운다  
            A[j] = 1;  
        }
    }
}    

루트 i < = N 는 아래와 같다.

i <= N * N

양변을 제곱한다. i의 루트가 사라진다. N은 제곱이 된다.

루트는 실수니까 오차를 발생시킬 수 있으므로 이렇게 제곱을 이용해 실수 사용을 피하는 것이다.

j는 i의 제곱부터 시작한다.

그 이유는 아래를 다시 읽어보면 된다.

처음에 2의 배수들을 지우면서 소수들의 x 2 는 이미 지워졌다.

또 3이 소수니까. 소수들의 x3은 이미 지워졌다.

이런식으로 지우다 보면 자신의 제곱 부터 지우면 된다.

2는 소수다. 2의 배수를 전부 지운다. -> 2x2, 2x3, 2x4, 2x5 .... 를 지운다

3은 소수다. 3의 배수를 전부 지운다. -> 3x2는 이미 2의 배수 지울 때 지워졌다. 3x3 부터 지운다.

4 ( 2 x 2) 는 2의 배수이므로 이미 지워졌다. 지나간다.

5는 소수다. 5의 배수를 전부 지운다.

​ -> 5x2, 5x3, 5x4 는 이미 2의배수, 3의배수, 4의배수 지울 때 지워졌다. 5x5 부터 지운다.

6 ( 3 x 2 ) 은 2의 배수이므로 이미 지워졌다. 지나간다.

맞은 코드

#include <iostream> 
#include <algorithm>
using namespace std;

int M, N;
int A[1000001] = { 0, };

int main(void){

    cin >> M >> N;

    A[0] = 1; // 0과 1은 소수가 아니다. 지운다. 
    A[1] = 1;

    for(int i = 2; i*i <= N; i++){

        if(A[i] == 0) {  

            for(int j = i*i; j <= N; j+=i ){ // i의 배수들을 전부 지운다  
                A[j] = 1;  
            }
        }
    }    

    for(int i = M; i <= N; i++){ // 0 으로 남은 소수 출력  
        if(A[i] == 0) {
            printf("%d\n", i);
        }
    } 

    return 0;    
} 

백준 1929번 문제 질문게시판에서 다양한 방식으로 풀어나가는 글을 읽어보며 도움이 많이 됬다.
정수를 곱할 때 int 표현범위를 오버플로우 하지 않도록 주의해야 한다는 점도 배웠다.

728x90

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

소인수분해 백준 11653번  (0) 2020.09.14
골드바흐의 추측 백준 6588번  (0) 2020.09.14
소수 찾기 백준 1978번  (0) 2020.09.13
팩토리얼 0의 개수 백준 1676번 c++  (0) 2020.09.12
팩토리얼 백준 10872번  (0) 2020.09.12

문제

소수 찾기 백준 1978번

리뷰

소수는 약수가 1과 자기 자신만 있는 수다.

N이 소수가 되는 조건

2이상이고 루트 N보다 작거나 같은 자연수로 나누어 떨어지는지 확인하면 된다.

N이 소수가 아니라고 가정하자.

소수가 아닌 N은 다음과 같이 표현할 수 있다.

N = a x b ( b는 a보다 큰 어떤 수.)

두 수 a와 b의 차이가 가장 작은 경우는 루트 N이다.

따라서 2부터 N까지가 아니라, 2부터 루트 N까지만 검사해보면, 소수인지 여부를 알 수 있다.

a가 루트N보다 크고, b가 루트N보다 크면,

a x b 도 N 보다 크다.

예를 들어 N = 24 이고,  a = 2, b = 12 

2 4 (여기 루트N 자리) 6 8 10 12 14 16 18 20 22 24

루트 24 는 4.xx 와 가까운 수가 될 것이다. 
a와 b 둘 중 하나는 루트 N 보다는 작다. 

코드로 나타내면 다음과 같다.

bool isPrime(int n){

    if(n < 2){
        return false;
    }

    for(int i = 2; i*i <= n; i++){ // 실수는 오차를 발생시킬 수 있으므로, i*i <= N 으로 계산하자.
        if(n % i == 0){
            return false;
        }
    }
    return true;
}

루트i < = N 는 아래와 같다.

i <= N * N

양변을 제곱한다. i의 루트가 사라진다. N은 제곱이 된다.

루트는 실수니까 오차를 발생시킬 수 있으므로 이렇게 제곱을 이용해 실수 사용을 피하는 것이다. 

어떤 수가 소수인지 확인하는데 걸리는 시간 복잡도는 O(루트N) 이다.

코드

#include <iostream> 
using namespace std;

bool isPrime(int n){

    if(n < 2){
        return false;
    }

    for(int i = 2; i*i <= n; i++){
        if(n % i == 0){
            return false;
        }
    }
    return true;
}

int main(void){

    int CNT = 0;
     int answer = 0;

    cin >> CNT;

    while(CNT--){
        int num = 0;
        cin >> num;
        if(isPrime(num)) answer++;
    }

    cout << answer;
    return 0;    
}
728x90

문제

팩토리얼 0의 개수 백준 1676번

리뷰

숫자 뒤 쪽에 0의 개수가 몇개가 되냐는건 소인수분해 했을 때 10이 몇번 곱해졌느냐에 따라 결정된다.

예를 들어, (팩토리얼 값은 아니지만) 34200 는 10이 2번 곱해진 것이다. 따라서 답은 2다. 2059000은 답이 3이다.

그런데, 10은 2x5 이다.

주어진 N을 5로 계속 나누는데, 나누어 떨어지는 개수를 세면 된다. == N! 을 소인수분해 했을때 5가 몇 승일까.

2의 개수를 안세도 되는 이유

10은 2x5니까 2의 개수도 세야 하지 않을까? 했다.
하지만, 소인수분해 했을 때 2를 인수로 가진 수의 개수 보다는, 5를 인수로 가진 수의 개수가 훨씬 적다.

7 팩토리얼을 예로 든다. 

7! = 7 x 6 x 5 x 4 x 3 x 2 x 1 

5의 배수는 딱 1개 
2의 배수는 3개 (2,4,6) 

예를 들어, 125 팩토리얼을 구한다

5 * 25 * 125 까지만 곱해봐도 5로 나누어 떨어지는 것의 개수를 셀 수 있다.

125!

125/5 =>  25개
25/5  =>  5개
5/5  =>  1개
따라서 답은 31이 된다. 

따라서 반복문의 i는 5씩 곱하면서 증가시킨다.

KSJ14님의 해설을 읽고 도움받았다.

코드

#include <iostream> 
using namespace std;

int main(void){
     int N = 0;
    int cnt = 0;

     cin >> N;

    for(int i = 5; i <= N; i *= 5){
        cnt += (N/i);
    }

    cout << cnt;

    return 0;    
}
728x90

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

소수 구하기 백준 1929번 에라토스테네스의 체  (0) 2020.09.14
소수 찾기 백준 1978번  (0) 2020.09.13
팩토리얼 백준 10872번  (0) 2020.09.12
스타트와 링크 백준 14889번  (0) 2020.09.10
Two Dots 백준 16929번  (0) 2020.09.09

문제

팩토리얼 백준 10872번


리뷰

정수 N(0 ≤ N ≤ 12)가 주어진다. 이때, N!을 출력하는 프로그램을 작성하는 문제다.

0! 은 0이라고 틀리게 알아서 처음에 풀 때 틀렸다. 질문게시판 FAQ 글 읽고 알았다.

0! 은 1이다

그 이유는 다음과 같다.

4! = 4 x 3 x 2 x 1 
    = 4 x (4-1)!
    = 4 x 3!

위의 논리를 1 팩토리얼에 적용해본다.

1! = 1 x (1-1)!
    = 1 x 0! = 1
    = 1! 은 1이다.  ( 1! = 1 )
    따라서 0! == 1 

코드

#include <iostream> 
using namespace std;

int main(void){

    int N = 0;
    long long answer = 1;
    cin >> N;

    for(int i = 1; i <= N; i++){
        answer = answer * i;    
    }
    cout << answer;

    return 0;    
}
728x90

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

소수 찾기 백준 1978번  (0) 2020.09.13
팩토리얼 0의 개수 백준 1676번 c++  (0) 2020.09.12
스타트와 링크 백준 14889번  (0) 2020.09.10
Two Dots 백준 16929번  (0) 2020.09.09
섬의 개수 백준 4963번  (0) 2020.09.08

문제

스타트와 링크 백준 14889번


리뷰

N개 중에 중복없이 N/2 개를 고른다. N과 M 문제를 다시 풀어보고 이 문제를 풀었다.

1 2 3 4 번 사람이 있으면, (총 인원 4명)

1 2 이렇게 두 명만 A팀으로 고르면 3 4 는 자동으로 B팀으로 정해진다.

그리고 1 2 를 고르면 다음에는 1 3 을 선택해야 한다.

그런데 두 명을 고르는데, 3번 사람 부터 고를 때는. 3 1 이 아니라 3 4 를 골라야 한다.

3번 사람을 고른 상태에서는, 4번 사람만 고를 수 있다.

앞서 고른 수보다 큰 수를 골라야 1 3, 3 1 처럼 중복 없이 고를 수 있다. N과 M 2에서 풀었던 코드의 구조를 따왔다. (조합)


N/2 명을 고르면 팀이 나눠진 것이다.

그 다음에는 벡터 A, B 에 숫자를 넣어두고, power 배열의 인덱스를 이용해서 능력치의 합을 구한다.

abs 함수로 절대값을 구해 처리했고 min 함수로 차이의 최소값를 계산했다.


코드

#include <iostream>
#include <algorithm> 
#include <vector>
using namespace std;

int N; 
int ch[21]; // 사람 선택 할 때 중복 방지  
int map[21][21];
int mindiff = 2e9;

void go(int index, int cnt){

    if(cnt == N/2){ //  N/2만큼 골랐다면, 팀 나눈다.  

         vector<int> S, L;
         int Ssum = 0, Lsum = 0;

         for(int i = 0; i <N; i++){ // 팀 나눠서 벡터에 담는다.  
             if(ch[i]) S.push_back(i);
             else L.push_back(i);
        }

        for(int i = 0; i < S.size(); i++){ // 팀별로 능력치 합  
            for(int j = i+1; j < S.size(); j++){
                Ssum += (map[S[i]][S[j]] + map[S[j]][S[i]]);
                Lsum += (map[L[i]][L[j]] + map[L[j]][L[i]]);
            }
        }

        mindiff = min(mindiff, abs(Ssum-Lsum) ) ; // 차이값 갱신 
        return;
    }

    for(int i = index; i < N; i++){ // index 자리의 사람 선택 
        if(ch[i]) continue; // 이미 선택한 사람이면 지나감 

        ch[i] = 1; // 선택 
        go(i+1, cnt+1); // 다음 사람 선택하러 재귀호출 
        ch[i] = 0; // 선택 안 함.
    }

}

int main(void){

    cin >> N;

     for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
             scanf("%d", &map[i][j]);
        }
    }

     go(0, 0);

     cout << mindiff;

    return 0;
} 
728x90

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

팩토리얼 0의 개수 백준 1676번 c++  (0) 2020.09.12
팩토리얼 백준 10872번  (0) 2020.09.12
Two Dots 백준 16929번  (0) 2020.09.09
섬의 개수 백준 4963번  (0) 2020.09.08
나이트의 이동 백준 7562번  (0) 2020.09.08

문제

Two Dots 백준 16929번

리뷰

처음에 BFS로 시도했는데, 문제의 에제 입출력 4개중에 3개만 맞아서 멘붕이 왔다.

3 4
AAAA
ABCA
AADA // 이건 사이클이 없는데. "Yes"를 출력한다... 

그래서 사이클 관련 질문게시판 글을 읽고 DFS로 시도해봤다.

시작지점을 목적지점 삼아서 탐색을 했다.

사이클은 시작점과 끝점이 같다고 생각할 수 있다.

그런데, 시작점 부터 방문 배열에 1로 체크해두면, 방문 안 해본 곳을 찾아서 탐색하기에는 시작점이자 끝점에 되돌아갈 수 없다. 그래서 이미 방문했더라도, 목적지인지 검사했다. 

 

일단 시작할 때 시작점 자체가 cnt 1개니까 1로 시작한다.

for(int i = 0; i  < N ; i++){  // 모든 지점에서 DFS 시작
    for(int j = 0; j < M ; j++){

        start_x = i; // 목적지 설정  
        start_y = j;

        DFS(i, j, 1); // 시작점에서 이미 cnt = 1 로 시작 

        if(stop_flag) break;
    }
} 

DFS를 시작하면, 방문 표시를 해두고, 상하좌우 4방향을 검사한다.

이 때, 아래의 3가지 조건을 만족하면 다음 칸으로 이동한다.

if(!boundary(next_x, next_y)) continue;  // 범위 내 인지  

if(B[start_x][start_y] != B[next_x][next_y]) continue; // 색깔이 같은지  

if(check[next_x][next_y] == 0){ // 미방문인지  

만약, 세번째 if 문에서 조건을 만족시키지 않고, 방문 했더라도, 목적지라면 탐색을 끝내야한다.

여기서 중요한 건 cnt가 4 이상인지도 함께 확인해야 한다는 것이다.

    if(check[next_x][next_y] == 0){ // 미방문인지  

        check[next_x][next_y] = 1;
        DFS(next_x, next_y, cnt+1); // 방문한다  
        check[next_x][next_y] = 0;

    }else{ // 목적지인지

        if(next_x == start_x && next_y == start_y && cnt >= 4){  // 4번 이상 움직여서 목적지 도달 
        stop_flag = true; // 탐색 끝!
        return;
    }
} 

맞는데 왜틀리지 ㅠㅠ 하고 골머리썩다가 BOJ 질문게시판에 글올려서 고수분들의 도움을 받았다. 정말 감사했다.

속시원했다.

코드

#include <iostream> 
#include <queue>
#include <algorithm>  
using namespace std;

int N, M;   // 행, 열  
char B[51][51]; //지도  
int check[51][51]; //방문 표시 지도

bool stop_flag = false;
int start_x, start_y; // 시작점을 '목적지'로 이용한다.  

// 4방향 
int dx[5] = {-1, 1, 0, 0}; 
int dy[5] = {0, 0, -1, 1};


// 지도 범위 내에 있는지 확인
bool boundary(int i, int j){   
    return (i >= 0 && i < N && j >= 0 && j < M) ? 1:0;
} 

void DFS(int now_x, int now_y, int cnt){ 

    if(stop_flag) return;

    check[now_x][now_y] = 1; // 방문  

    for(int i = 0; i<4; i++){ // 4방향 확인 

        int next_x = now_x + dx[i];
        int next_y = now_y + dy[i];

        if(!boundary(next_x, next_y)) continue;  // 범위 내 인지  

        if(B[start_x][start_y] != B[next_x][next_y]) continue; // 색깔이 같은지  

        if(check[next_x][next_y] == 0){ // 미방문인지  

            check[next_x][next_y] = 1;
            DFS(next_x, next_y, cnt+1); // 방문한다  
            check[next_x][next_y] = 0;

        }else{
            // 목적지인지  
            if(next_x == start_x && next_y == start_y && cnt >= 4){  // 4번 이상 움직여서 목적지 도달 
                stop_flag = true;
                return;
            }
        } 

    }

} 

int main(void){
     // freopen("input.txt", "rt", stdin);
     cin >> N >> M; 

    for(int i = 0; i <N; i++){
        for(int j = 0; j < M; j++){
            cin >> B[i][j];
        }
    }  // 입력받기 끝 

    string res = "";


    for(int i = 0; i  < N ; i++){  // 모든 지점에서 DFS 시작
        for(int j = 0; j < M ; j++){

            start_x = i; // 목적지 설정  
            start_y = j;

            DFS(i, j, 1); // 시작점에서 이미 cnt = 1 로 시작 

            if(stop_flag) break;
        }
    } 

    if(stop_flag) cout << "Yes";
    else cout << "No";

    return 0;    
}
728x90

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

팩토리얼 백준 10872번  (0) 2020.09.12
스타트와 링크 백준 14889번  (0) 2020.09.10
섬의 개수 백준 4963번  (0) 2020.09.08
나이트의 이동 백준 7562번  (0) 2020.09.08
사탕게임 백준 3085번 c++  (0) 2020.09.07

+ Recent posts