[BOJ] 수학 2(1)

2020. 7. 25. 04:56BOJ

오늘은 수학 2단계의 전반부를 풀어보겠습니다. 소수에 관한 문제들입니다. 이번 시간에 소수에 관한 문제를 풀 때는 알고리즘을 2개만 알면 됩니다.

 

먼저 하나의 소수가 주어졌을 때 해당 수가 소수인지 아닌지 판단하는 경우입니다.

bool isprime(int n){
    if(n==1) return false;
    for(int k=2;k*k<=n;++k)
        if(n % k == 0)
            return false;
    return true;
}

일일이 나머지가 0인 게 있는지 확인해보는 것입니다. 실제 검사를 할 때는 제곱이 n보다 작거나 같은 경우까지만 검사하면 됩니다. 그 이후에 약수가 있는 경우 그 이전에도 반드시 약수가 있기 때문입니다.

 

그다음으로는 에라토스테네스의 체라는 것입니다. 

class primesift{
    public:
        vector<bool> isprime;
        primesift(int lim) : isprime(lim + 1, true){
            isprime[0] = false;
            isprime[1] = false;
            for(int p=2;p*p<=lim;++p){
                for(int k=2*p;k<=lim;k+=p)
                    isprime[k] = false;
            }
        }
};

위와 같이 2중 for문으로 구해주었습니다. 바깥 for문은 첫 번째 알고리즘에서 하듯이 여러 숫자를 돌면서 검사를 해주는 겁니다. 내부 for문은 해당 숫자의 배수들에 대해서 소수가 아님을 확인하고 false로 바꿔주는 겁니다. 

 

이렇게 설명했으면 더 이상 설명은 필요할 것 같지 않네요. 코드를 보고 마치겠습니다.

 

[BOJ 1978]

 

#include <iostream>

using namespace std;

bool isprime(int n){
    if(n==1) return false;
    for(int k=2;k*k<=n;++k)
        if(n % k == 0)
            return false;
    return true;
}

int main() {
    int N;
    scanf("%d",&N);
    int count = 0;
    for(int k=0;k<N;++k){
        int n;
        scanf("%d",&n);
        if(isprime(n))
            count++;
    }
    printf("%d",count);
}

 

[BOJ 2581]

 

#include <stdio.h>
#include <vector>

using namespace std;

class primesift{
    public:
        vector<bool> isprime;
        primesift(int lim) : isprime(lim + 1, true){
            isprime[0] = false;
            isprime[1] = false;
            for(int p=2;p*p<=lim;++p){
                for(int k=2*p;k<=lim;k+=p)
                    isprime[k] = false;
            }
        }
        int intervalsum(const int L, const int R){
            int primesum = 0;
            for(int k=L;k<=R;++k){
                if(isprime[k]){
                    primesum += k;
                }
            }
            return primesum;
        }
        int intervalmin(const int L, const int R){
            for(int k=L;k<=R;++k){
                if(isprime[k]){
                    return k;
                }
            }
            return -1;
        }
};

int main(){
    int M, N;
    scanf("%d%d",&M,&N);
    
    primesift P(N);
    int ressum = P.intervalsum(M, N);
    if(ressum>0)
        printf("%d\n%d\n",ressum, P.intervalmin(M, N));
    else
        printf("-1\n");
}

 

[BOJ 1929]

 

정답 풀이들을 보니까 이거보다 더 최적화를 하신 분들이 있더라고요. 요약하면 구간합 알고리즘을 약간 섞어주면 됩니다. 구간합 알고리즘은 처음부터 누적합을 구해놓은 다음에 필요한 구간의 양 끝의 누적 값의 차이로 구하는 겁니다. 이렇게 구하면 각 구간을 일일이 검사할 필요 없이 한방에 구할 수 있게 됩니다. 

#include <stdio.h>
#include <vector>

using namespace std;

class primesift{
    public:
        vector<bool> isprime;
        primesift(int lim) : isprime(lim + 1, true){
            isprime[0] = false;
            isprime[1] = false;
            for(int p=2;p*p<=lim;++p){
                for(int k=2*p;k<=lim;k+=p)
                    isprime[k] = false;
            }
        }
};

int main(){
    int M, N;
    scanf("%d%d",&M,&N);
    
    primesift P(N);
    for(int k=M;k<=N;++k){
        if(P.isprime[k]){
            printf("%d\n",k);
        }
    }
}

 

[BOJ 4948]

 

#include <stdio.h>
#include <vector>

using namespace std;

class primesift{
    public:
        vector<bool> isprime;
        primesift(int lim) : isprime(lim + 1, true){
            isprime[0] = false;
            isprime[1] = false;
            for(int p=2;p*p<=lim;++p){
                for(int k=2*p;k<=lim;k+=p)
                    isprime[k] = false;
            }
        }
        int intervalcount(const int L, const int R){
            int primecount = 0;
            for(int k=L;k<=R;++k){
                if(isprime[k]){
                    primecount++;
                }
            }
            return primecount;
        }
};

int main(){
    vector<int> inputs;
    int IN, INMAX = 0;
    while(true){
        scanf("%d",&IN);
        if(IN == 0) break;
        inputs.push_back(IN);
        if(IN > INMAX)
            INMAX = IN;
    }
    
    primesift P(INMAX*2);
    for(const int IN : inputs){
        printf("%d\n",P.intervalcount(IN+1, IN*2));
    }
}

 

[BOJ 9020]

 

#include <stdio.h>
#include <vector>

using namespace std;

class primesift{
    public:
        vector<bool> isprime;
        primesift(int lim) : isprime(lim + 1, true){
            isprime[0] = false;
            isprime[1] = false;
            for(int p=2;p*p<=lim;++p){
                for(int k=2*p;k<=lim;k+=p)
                    isprime[k] = false;
            }
        }
        int goldbach(const int P){
            for(int n=P/2;n>=2;--n){
                if(isprime[n] && isprime[P - n]){
                    return n;
                }
            }
            return -1;
        }
};

int main(){
    int T;
    scanf("%d",&T);
    
    vector<int> inputs(T);
    int IN, INMAX = 0;
    for(int k=0;k<T;++k){
        scanf("%d",&IN);
        inputs[k] = IN;
        if(INMAX < IN)
            INMAX = IN;
    }
    
    primesift P(INMAX);
    for(const int IN : inputs){
        int partition = P.goldbach(IN);
        printf("%d %d\n",partition, IN - partition);
    }
}

 

수고하셨습니다.

'BOJ' 카테고리의 다른 글

[BOJ 19533] [UCPC 2020 예선] 길이 문자열  (0) 2020.07.28
[BOJ] 수학 2(2)  (0) 2020.07.25
[BOJ] 수학 1(2)  (0) 2020.07.24
[BOJ] 수학 1(1)  (0) 2020.07.24
[BOJ] 문자열  (0) 2020.07.23