728x90
반응형

문제 해설

이 문제는 a는 S에서 출발하여 A까지 가야하고, b는 S에서 출발하여 B까지 도착해야 한다. 그리고 만약 같이 합승한다면 돈은 한번만 내도 된다.
처음에는 진짜 너무 복잡하게 생각했었는데, S->A로 가는 모든 경로를 구하고 S->B로 가는 모든 경로를 구한다음.. 서로 조합을 해서 같은 경로를 구하고.. 같으면 비용 안내도 되니까 한번만 계산해서 그 중 최솟값을 구할려고 했다...

이걸 굳이 각각 나누어서 경로를 구할 필요가 없었다. 결국에는 어떤 노드까지는 a와 b가 같이 가고, 그 다음 그 노드에서 a는 A까지, b는 B까지 가면 된다. 만약 x->y로 가는 최소비용을 알고 있으면 쉬워진다. 그럼 1부터 n까지의 노드들을 다 탐색하여 같이 가는 노드를 구한 다음, a,b 따로 가는 비용을 구해주면 된다.

여기서 x->y로 가는 최소비용을 구하는 방법은 2가지이다. 다익스트라를 이용해서 구할 수 있고, 모든 비용을 구해야하기 때문에 플로이드 워셜을 사용해도 된다.

다익스트라를 이용한 방법

플로이드 워셜보다 다익스트라를 먼저 생각한 것은 시간 효율성 때문이다. 다익스트라는 시간복잡도가 O(ElogV)이기 때문에 여기서 n은 200이하의 자연수이기 때문에 O(E)가 나온다. 즉, 간선의 개수와 시간복잡도가 같다. (O(Elog200) -> O(2E) -> O(E))

파이썬

import heapq

def solution(n, s, a, b, fares):
    INF = int(1e9)
    answer = INF

    def dijkstra(start):
        dist = [INF for _ in range(n + 1)]
        dist[start] = 0
        q = []
        heapq.heappush(q, [dist[start], start])
        while q:
            cur_dist, node = heapq.heappop(q)
            for tmp_node, tmp_dist in cost[node]:
                if dist[tmp_node] > cur_dist + tmp_dist:
                    dist[tmp_node] = cur_dist + tmp_dist
                    heapq.heappush(q, [dist[tmp_node], tmp_node])
        return dist

    cost = [[] for _ in range(n + 1)]
    for start, end, val in fares:
        cost[start].append((end, val))
        cost[end].append((start, val))

    dist = []
    for i in range(n + 1):
        dist.append(dijkstra(i))
    # print(dist)
    for i in range(1, n+1):
        answer = min(dist[s][i] + dist[i][a] + dist[i][b], answer)
    return answer
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;

class Solution {
    class Node implements Comparable<Node> {
        int node;
        int distance;

        public Node(int node, int distance) {
            this.node = node;
            this.distance = distance;
        }

        @Override
        public int compareTo(Node o) {
            return Integer.compare(this.distance, o.distance);
        }
    }

    public int solution(int n, int s, int a, int b, int[][] fares) {
        int answer = Integer.MAX_VALUE;

        ArrayList<Node>[] cost = new ArrayList[n + 1];
        for (int i = 1; i <= n; i++) {
            cost[i] = new ArrayList<>();
        }

        for (int[] fare : fares) {
            int start = fare[0];
            int end = fare[1];
            int val = fare[2];
            cost[start].add(new Node(end, val));
            cost[end].add(new Node(start, val));
        }

        int[][] dist = new int[n + 1][n + 1];
        for (int i = 1; i <= n; i++) {
            Arrays.fill(dist[i], Integer.MAX_VALUE);
        }

        for (int i = 1; i <= n; i++) {
            dijkstra(i, n, cost, dist[i]);
        }

        for (int i = 1; i <= n; i++) {
            answer = Math.min(answer, dist[s][i] + dist[i][a] + dist[i][b]);
        }

        return answer;
    }

    public void dijkstra(int start, int n, ArrayList<Node>[] cost, int[] dist) {
        PriorityQueue<Node> pq = new PriorityQueue<>();
        pq.add(new Node(start, 0));
        dist[start] = 0;

        while (!pq.isEmpty()) {
            Node cur = pq.poll();

            for (Node next : cost[cur.node]) {
                int newDist = dist[cur.node] + next.distance;
                if (newDist < dist[next.node]) {
                    dist[next.node] = newDist;
                    pq.add(new Node(next.node, newDist));
                }
            }
        }
    }
}

플로이드-워셜을 이용한 방법

플로이드 워셜은 노드들을 거치는 최단거리를 모두 다 구하게 된다. 그래서 노드들을 3번 탐색하게 되어 시간복잡도가 O(N^3)이 나오게 된다.
이 방법은 시간초과를 항상 유념해야 하는데, 문제조건에서 N은 200이하이기 때문에 O(10^6)정도가 나오게 되어 가능하다.

파이썬

def solution(n, s, a, b, fares):
    INF = int(1e9)
    cost = [[INF for _ in range(n + 1)] for _ in range(n + 1)]
    for i in range(1, n + 1):
        cost[i][i] = 0
    for fare in fares:
        start = fare[0]
        end = fare[1]
        val = fare[2]
        cost[start][end] = val
        cost[end][start] = val
    for k in range(1, n + 1):
        for i in range(1, n + 1):
            for j in range(1, n + 1):
                cost[i][j] = min(cost[i][j], cost[i][k] + cost[k][j])
    answer = INF
    for i in range(1, n + 1):
        answer = min(answer, cost[s][i] + cost[i][a] + cost[i][b])
    return answer

자바

import java.util.Arrays;
class Solution {
    static int[][] cost;
    public int solution(int n, int s, int a, int b, int[][] fares) {
        cost = new int[n + 1][n + 1];

        for (int i = 1; i <= n; i++) {
            Arrays.fill(cost[i], 1000000);
        }

        for (int[] fare : fares) {
            int start = fare[0];
            int end = fare[1];
            int val = fare[2];
            cost[start][end] = val;
            cost[end][start] = val;
        }

        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    if (i != j) {
                        cost[i][j] = Math.min(cost[i][j], cost[i][k] + cost[k][j]);
                    }
                }
            }
        }
        for (int i = 1; i <= n; i++) {
            cost[i][i] = 0;
        }

        int answer = Integer.MAX_VALUE;
        for (int i = 1; i <= n; i++) {
            answer = Math.min(answer, cost[s][i] + cost[i][a] + cost[i][b]);
        }

        return answer;
    }
}

정답 및 후기

python은 두 가지 방법 모두 통과하였지만, java의 경우 다익스트라에서 효율성에서 4개를 틀렸었다.
원인은 시간복잡도 때문인데, 시간초과가 나는 이유는 다익스트라에서 간선이 많은 경우에 문제가 생기는 것 같다.
간선이 많으면 위의 문제에서 시간복잡도가 초과될 수 있다.

항상 음수간선이 없으면 플로이드-워셜보다 다익스트라를 먼저 생각했는데(일반적으로 간선보다 노드 개수에 제한이 많은 경우가 많아서..)
이 경우는 노드가 200이하라 플로이드-워셜이 더 효과적인 문제였다.

728x90
반응형

'알고리즘 > 알고리즘 문제' 카테고리의 다른 글

[알고리즘] 2048 (Easy)  (0) 2023.10.08
[알고리즘] 친구비  (2) 2023.06.03
[백준] 타임머신 11657  (0) 2023.03.22
[백준 2636] 치즈  (0) 2023.03.11
[백준 - 1062] [파이썬] 가르침  (0) 2023.01.08
728x90
반응형

(https://school.programmers.co.kr/learn/courses/30/lessons/214289)

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

[문제 정보]

먼저 온도가 여러 개 나오는데 실내온도, 실외온도, 희망온도가 주어지고, 또 승객들 등등 여러 가지 정보가 한번에 나온다. 한번에 읽으면 정리가 안되기 때문에 어떤 걸 해결해야하는지 고민해봐야 한다. 

 

먼저 온도가 변하는 것을 생각하지 말고 온도가 어떤 범위 내에 있어야 하는지를 생각해보자. 승객이 있을 때는 온도는 t1~t2에 있어야 한다. 그리고 승객이 없을 때는 온도가 어디든지 사실 상관이 없다. 그러나 문제는 온도는 1도씩밖에 바뀌지 않기 때문에 승객이 있기 1분 전에는 반드시 t1-1에 있거나 t2+1에 있어야 승객이 있을 때 범위 내에 있게 된다. 

 

이번엔 온도가 어떻게 변하는지 살펴보겠다. 일단 에어컨이 꺼져 있을 경우, 온도는 실외온도를 따라간다. 에어컨을 껐는데 실외온도에 차이가 없다면 온도는 그대로 유지가 된다.

에어컨이 켜져 있을 경우엔 2가지 이다. 에어컨을 켜고 온도를 유지하는 경우와 에어컨을 켜서 실내온도와 반대 방향으로 1도 변경하는 경우이다. 

이렇게 단계적으로 생각해보면 희망온도는 사실 의미가 없다. 에어컨을 켜서 온도를 변하게 할 것인지 아니면 유지할 것인지의 문제일 뿐이다.

  • 에어컨 X, 실내온도 방향으로 1도 변경하는 경우(비용 0)
  • 에어컨 X, 온도를 유지하는 경우(비용 0)
  • 에어컨 O, 온도를 유지하는 경우(비용 b)
  • 에어컨 O, 온도를 실내온도와 반대 방향으로 1도 변경하는 경우(비용 a)

여기까지 이해하고 문제를 풀면 된다.

[문제 해설]

먼저 시간의 흐름이 있고, 온도의 변화도 있으므로 dp 2차원을 생각해내야 한다. 시간이 흐름에 따라 온도가 어떻게 변화하는지 확인하여 최적의 비용을 계산할 수 있기 때문이다.

 

DP를 사용하려면 문제가 있다. 지금 온도 범위는 -10도에서 40도까지인데 2차원 배열의 인덱스에는 음수가 들어갈 수 없다는 점이다. 그래서 온도를 임의로 10도씩 올려서 계산한다. 

 

그리고 dp를 사용하여 int(1e9)의 큰 값으로 모든 값들을 초기화 한다.

처음의 온도는 실외온도와 같다고 하였으므로 dp[0][temperature]은 0으로 초기화해준다. (여기가 출발점이다.)

 

그 이후에는 dp를 1부터 주어진 onboard의 길이까지 진행하면 된다. 

 

승객이 있을 경우는 dp 온도를 t1에서 t2만 구하면 된다. 그리고 승객이 없다면 나머지를 굳이 구할 필요없이 min(t1, 실외온도) 에서 max(t2, 실외온도)로 구하면 된다. 실외온도는 t1~t2 바깥 값이므로 이렇게 범위를 구하면 된다. 온도가 계속하여 이 사이의 값에 있어야 효율적임을 알 수 있다. 이유는 예시를 하나 생각해보면 된다. t1이 10도 이고, 실외온도가 8도라고 가정하면 8도 이상이어야 된다. 에어컨을 사용하여 8도 밑으로 내려갈 이유가 없다. (에어컨의 소비전력이 최소화가 되지 않는다)

 

그래서 먼저 범위를 구해주고 범위내의 dp값을 업데이트하는 식을 통해서 진행하면 된다. 

 

이 업데이트 하는 식이 조금 고민해야 한다. 

for j in range(start, end + 1):
    if j - 1 < 0:
        x = int(1e9)
    else:
        x = dp[i - 1][j - 1]
    if j + 1 > 50:
        y = int(1e9)
    else:
        y = dp[i - 1][j + 1]
    if j < temperature:
        dp[i][j] = min(x, y + a, dp[i - 1][j] + b)
    elif j > temperature:
        dp[i][j] = min(x + a, y, dp[i - 1][j] + b)
    elif j == temperature:
        dp[i][j] = min(x, y, dp[i - 1][j])

여기서 먼저 j-1이 0보다 작거나 j+1이 50을 넘어가면 계산이 안되므로 해당 값들을 처리해준다. 

 

먼저 j가 실외온도보다 낮을때 처리하는 경우를 생각해보자. dp[i][j]는 i분에 j온도가 되기 위한 비용이다. 

첫번째 먼저 i-1분에 j-1온도일 때는 실외온도를 따라가므로 에어컨이 없더라도 1도 올라간다. 

두번째 i-1분에 j+1온도일 때는 실외온도를 따라가면 j+2가 되거나 j+1이 유지(실외온도가 j+1이라면)되므로 j가 되기 위해서는 에어컨을 틀어서 온도를 낮춰야 한다. 

세번째 i-1분에 j온도일때는 유지만 하면 된다. 만약 에어컨을 끄면 실외온도를 따라가서 온도가 올라가므로 유지하려면 에어컨을 켜서 유지해야 한다. 에어컨이 유지하는 비용은 b라고 명시해주었다. 따라서 식은 다음과 같다. 여기서 x는 dp[i-1][j-1], y는 dp[i-1][j+1]이라고 생각하면 된다.

if j < temperature:
    dp[i][j] = min(x, y + a, dp[i - 1][j] + b)

 

그 다음에는 j가 실외온도보다 높을때 살펴보겠다. 

먼저 현재온도가 j-1이라면 j가 되려면 에어컨을 켜야한다. 아니면 실외온도를 따라가므로 온도가 떨어지거나 유지되기 때문이다.

두번째 j+1온도라면 실외온도를 그냥 따라가면 내려가므로 에어컨을 꺼도 된다.

세번째 j온도라면 유지를 해야한다. 그럼 그냥 b 비용을 내면된다. 이 세 개중에서 최소가 dp[i][j]가 된다.

elif j > temperature:
    dp[i][j] = min(x + a, y, dp[i - 1][j] + b)

 

마지막으로 j와 실외온도가 같을때 이다.

첫번째 현재온도가 j-1이라면 실외온도를 따라 올라가면 되므로 에어컨이 필요없다.

두번째 현재온도가 j+1이라면 실외온도를 따라 내려가면 되므로 에어컨이 필요없다.

세번쨰 현재온도가 j라면 실외온도 현재온도 목표온도가 다 같으므로 그냥 나두면 된다.

elif j == temperature:
    dp[i][j] = min(x, y, dp[i - 1][j])

 

이렇게 점화식을 계산하고 마지막 dp[i][j]에서 i가 onboard의 마지막일때 가능한 비용들이 나온다. 이 중 최소값이 정답이 되게 된다.

[코드]

def solution(temperature, t1, t2, a, b, onboard):
    temperature = temperature + 10
    t1 = t1 + 10
    t2 = t2 + 10

    dp = [[int(1e9)] * 51 for _ in range(len(onboard))]
    dp[0][temperature] = 0
    for i in range(1, len(onboard)):
        if onboard[i] == 1:
            start = t1
            end = t2
        else:
            start = min(t1, temperature)
            end = max(t2, temperature)
        for j in range(start, end + 1):
            if j - 1 < 0:
                x = int(1e9)
            else:
                x = dp[i - 1][j - 1]
            if j + 1 > 50:
                y = int(1e9)
            else:
                y = dp[i - 1][j + 1]
            if j < temperature:
                dp[i][j] = min(x, y + a, dp[i - 1][j] + b)
            elif j > temperature:
                dp[i][j] = min(x + a, y, dp[i - 1][j] + b)
            elif j == temperature:
                dp[i][j] = min(x, y, dp[i - 1][j])
        if i == len(onboard) - 1:
            return min(dp[len(onboard) - 1])

[후기]

실제 현대모비스 알고리즘 대회 때 머리만 싸매다가 못풀었었는데.. 다시 풀어도 머리가 터질 뻔했었다.
어려운 알고리즘이 있는 것보다 더 어려운 문제가 아닐까..(dp만 알면 풀 수는 있는)
그래도 좋은 문제 같아서 나중에 코딩 테스트 전에 한번 풀어보면 또 좋을것 같다.

728x90
반응형
728x90
반응형

https://programmers.co.kr/learn/courses/30/lessons/12926

 

코딩테스트 연습 - 시저 암호

어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 AB는 1만큼 밀면 BC가 되고, 3만큼 밀면 DE가 됩니다. z는 1만큼 밀면 a가

programmers.co.kr

[문제 설명]

 

어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 AB는 1만큼 밀면 BC가 되고, 3만큼 밀면 DE가 됩니다. z는 1만큼 밀면 a가 됩니다. 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.

 

[제한 조건]

  • 공백은 아무리 밀어도 공백입니다.
  • s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.
  • s의 길이는 8000이하입니다.
  • n은 1 이상, 25이하인 자연수입니다.

입출력 예

s n result
"AB" 1 "BC"
"z" 1 "a"
"a B z" 4 "e F d"

 

[풀이 과정] : [풀이 1] -> [풀이 2]

 

[풀이 1] : 테스트 6,7,8,10,12 오류

 

먼저 string s에서 하나씩 체크해 준다. 만약 s에서 빈칸이면 빈칸을 반환해 준다. 빈칸이 아닐 경우, string 타입을 int로 바꾸어서 n을 더해준다. 

 

#include <string>
#include <vector>

using namespace std;

string solution(string s, int n) {
    string answer;
    for(int i=0;i<s.length();i++){
        if(s[i]==' ') answer+=' ';
        else{
            int temp=s[i]-0;
            temp+=n;
            if(temp>90&&temp<97) {temp= temp-26;}
            else if(temp>122) {temp = temp-26;}
            char ch = temp;
            answer += ch;
        }
    }
    return answer;
}

1. string을 int로 바꾸는 방법은 여러 가지가 있지만, 여기서는 s[i]-0을 int 변수에 대입했다.

2. temp변수에 n을 더하고 n을 더한 temp 값이 알파벳 소문자인 a~z 와 대문자인 A~Z를 초과했을 경우 다시 돌아갈 수 있게 차이인 26을 빼서 temp 값에 다시 저장하였다. (26은 아스키 값에서 z-a+1이다.)

3. temp값을 char형의 ch변수에 저장하고 answer에 ch를 저장하였다.

 

//아스키 코드 표 참고//

 

[풀이 2] 

[풀이 1]은 n의 개수가 너무 커져서 알파벳 소문자들과 대문자들의 차이를 정확히 구별하기 힘들다고 생각했다.

예를 들면 input 값이 z일때 n이 20이면 아스키 코드 값은 110이 된다. 그러나 소문자로 변환해 주어야 되는데 해당하는 조건이 없으므로 output값이 오류가 날 수 있을 것이라고 생각했다. 그래서 [풀이 2]는 이러한 케이스 들을 다시 처리해주었다.

<내가 직접 더해준 케이스들>

s n result
"AB" 1 "BC"
"z" 1 "a"
"Z" 10 "J"
"a B z" 4 "e F d"
"aBZ" 20 "uVT"

s[i]를 int로 바꾸어서 저장한 temp값을 소문자 대문자로 조건을 먼저 분류한 뒤, [풀이 1]의 방법을 사용하였다.

#include <string>
#include <vector>

using namespace std;

string solution(string s, int n) {
    string answer = "";
    for(int i=0;i<s.length();i++){
        if(s[i]==' ') answer+=' ';
        else{
            int temp = s[i]-0;
            if(temp>=65&&temp<=90){
                temp+=n;
                if(temp>90) temp-=26;
            }
            else if(temp>=97&&temp<=122){
                temp+=n;
                if(temp>122) temp-=26;
            }
            char ch = temp;
            answer+=ch;
        }
    }
    return answer;
}

 

728x90
반응형
728x90
반응형

https://programmers.co.kr/learn/courses/30/lessons/12921

 

코딩테스트 연습 - 소수 찾기

1부터 입력받은 숫자 n 사이에 있는 소수의 개수를 반환하는 함수, solution을 만들어 보세요. 소수는 1과 자기 자신으로만 나누어지는 수를 의미합니다. (1은 소수가 아닙니다.) 제한 조건 n은 2이상

programmers.co.kr

[문제 설명]

1부터 입력받은 숫자 n 사이에 있는 소수의 개수를 반환하는 함수, solution을 만들어 보세요.

소수는 1과 자기 자신으로만 나누어지는 수를 의미합니다.
(1은 소수가 아닙니다.)

[제한 조건]

  • n은 2이상 1000000이하의 자연수입니다.

입출력 예

n result
10 4
5 3

입출력 예 설명

입출력 예 #1
1부터 10 사이의 소수는 [2,3,5,7] 4개가 존재하므로 4를 반환

입출력 예 #2
1부터 5 사이의 소수는 [2,3,5] 3개가 존재하므로 3를 반환

 

[문제 개요]

 소수는 자신과 1이외의 정수로 나누어떨어지지 않는 정수이다. 예를 들어 소수인 13은 2,3,...,12 가운데 어떤 정수로도 나누어 떨어지지 않는다. 그러므로 어떤 정수 n에 대하여 아래의 조건을 만족하면 소수임을 알 수 있다. 

2부터 n-1까지의 어떤 정수로도 나누어떨어지지 않는다.

만약 나누어떨어지는 정수가 하나 이상 존재하면 그 수는 합성수(composite number)이다. 

 

[풀이 과정]

알고리즘 개선 과정 : [풀이 1] -> [풀이 2] -> [풀이 3],[풀이 4]

 

[풀이 1]

첫번째 풀이는 제일 처음에 풀었던 방식인데 테스트 12부터 시간 초과가 나고 효율성에서 0점을 받았다.. 

 

최대공약수 함수 gcd를 먼저 만들어 놓는다. 판별하고 싶은 수(n) 전까지 모두 gcd에서 return 1이 나오면 된다고 생각해서 풀었던 방식이다. temp 변수를 0으로 초기화 해두어서 gcd가 1이 나올경우 temp++해준다. 만약 temp가 n-1이 된다면 이 수를 '소수' 라고 판단해주는것이다. (할때부터 시간 초과가 날 것 같았고, 그냥 머릿속에 떠오른 대로 푼 방식이다)

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

#include <string>
#include <vector>

using namespace std;
int gcd(int a, int b){
    int c;
    while(b!=0){
        c=a%b;
        a=b;
        b=c;
    }
    return a;
}
int solution(int n) {
    int answer = 0;
    for(int i=2;i<=n;i++){
        int temp=0;
        for(int j=1;j<i;j++){
            if(gcd(i,j)==1){
                temp++;
            }else break;
        }
        if(temp==(i-1)) answer++;
    }
    return answer;
}

비슷한 경우 : 1000이하의 소수를 나열하는 프로그램.

#include <iostream>
using namespaces std;

int main(void){
    for(int n=2;n<=1000;n++){
    	for(int i=2;i<n;i++){
        	if(n%i==0)//나누어떨어지면 소수가 아니다
            	break;//더 이상의 반복은 불필요
        }
        if(n==i)
        	cout<<n<<endl;
    }
    return 0;
}

for문의 반복이 종료된 시점에서 변수의 값은 아래와 같다.


n이 합성수(소수가 아닌) 경우 : n보다 작은 값 (for문이 중단됨)

[풀이 2] : 테스트 15까지 통과했지만 효율성 0점..

위의 상자에서 힌트를 얻었다. n이 2또는 3으로 나누어 떨어지지 않으면 2X2인 4 또는 2X3인 6으로도 나누어떨어지지 않는다. 그러므로 이 프로그램은 불필요한 나눗셈을 실행하고 있음을 알 수 있다. 즉, 정수 n이 소수인지의 여부는 아래 조건을 만족하는지 조사하면 된다.

2부터 n-1까지의 어떤 소수로도 나누어떨어지지 않는다.

예를 들어, 7이 소수인지는 7보다 작은 소수 2,3,5로 나눗셈을 실행하면 충분하다. 이 아이디어를 이용하여 시간을 줄여보겠다.

#include <string>
#include <vector>

using namespace std;

int solution(int n) {
    int answer = 1;
    vector<int> prime;// 소수를 저장하는 배열
    prime.push_back(2);// 2는 소수 -> answer=1에서 시작
    for(int i=3;i<=n;i+=2){// 홀수만을 대상으로 한다
        int j;
        for(j=0;j<prime.size();j++){// 이미 구한 소수 배열의 크기까지 반복문
            if(i%prime[j]==0)// 나누어 떨어지면 소수가 아니다
                break;
        }
        if(j==prime.size()) {// 마지막까지 나누어떨어지지 않으면
            prime.push_back(i);// 배열에 저장한다.
            answer++;// 소수 개수는 증가시켜줌
        }
    }
    return answer;
}

소수를 구하는 과정에서 그때까지 구한 소수를 배열 prime의 요소로 저장한다. 이 때, n이 소수인지를 판단할 때 쌓아 놓은 소수로 나눗셈을 한다. 먼저 2는 소수이므로 prime에 저장한다. 3이상의 소수는 이중 for문으로 구한다. 바깥쪽 for문은 n의 값을 2씩 증가하여(짝수는 2의 배수이므로 해줄 필요 x) 3,5,7,9...,999로 홀수 값만을 생성한다.

 

[풀이 3] : 효율성까지 통과

100을 두 숫자의 곱셈으로 나타내면 어떻게 될까 -> ① 2x50 ② 4X25 ③ 5X20 ④ 10X10 ⑤ 20X5 ⑥ 25X4 ⑦ 50X2

여기서 ① ~ ④까지의 나눗셈만 수행하면 된다.이것은 넓이가 100인 직사각형의 가로,세로 길이와 같다.

예를 들어, 2X50과 50X2 는 가로, 세로가 다르지만 같은 직사각형으로 볼 수 있다. 100은 10X10을 중심으로 대칭 형태를 이루고 있다. 이는 소수임을 판단할때, 정사각형의 한 변의 길이까지만 소수로 나눗셈을 시도하면 된다는 뜻이다. ( 나머지는 대칭이라고 보면 된다) 이 과정에서 한 번도 나누어떨어지지 않으면 소수라고 판단할 수 있게 되는 것이다. 이러한 성질을 이용하여 제곱근을 한 변으로 하는 이후의 직사각형에 대한 계산량을 줄이는 것이 핵심이다. 

 즉, 어떤 정수 n은 다음 조건을 만족하면 소수라고 판단할 수 있다.

n이 소수일 조건은 n의 제곱근 이하의 어떤 소수로도 나누어떨어지지 않는다.

[풀이2]에서 prime[i]를 n의 제곱근 이하의 수들만 판단하면 된다는 것이다.

 예를 들어 첫 번째 경우에서 10이하의 소수를 구할려면 10의 제곱근은 3.xxx 이므로 3이하의 소수로 나누어 떨어지는지 판단하면 된다. 

#include <string>
#include <vector>

using namespace std;

int solution(int n) {
    vector<int> prime;// 소수를 저장하는 배열
    prime.push_back(2);// 2는 소수
    for(int i=3;i<=n;i+=2){// 홀수만을 대상으로 한다
        bool flag=true;//나누어 떨어지는지 확인할 변수
        for(int j=0;prime[j]*prime[j]<=i;j++){// prime[j]의 제곱이 i이하인가 판단
            if(i%prime[j]==0){// 나누어 떨어지면 소수가 아니다
                flag=false;
                break;
            }
        }
        if(flag) {// 마지막까지 나누어떨어지지 않으면
            prime.push_back(i);// 배열에 저장한다.
        }
    }
    return prime.size();
}

 

[풀이 4]

4번째 풀이는 알고리즘에 '에라토스테네스의 체' 라는 효율적인 방법으로 풀었다. 에라토스테네스의 체는 2부터 소수를 구하고자 하는 구간의 모든 수를 나열하여, 자기 자신을 제외한 배수들을 모두 지운다. 예를 들어 2부터 시작할 때, 2는 자기 자신을 제외한 2의 배수를 모두 지운다. 그 다음, 남아있는 수 가운데 3은 소수이므로 자기 자신을 제외한 3의 배수를 모두 지운다. 앞서 [풀이 3]에서 제곱근 만큼만 하면 나머지는 대칭이므로 위의 과정을 구하려는 n의 제곱근 이하의 자연수만큼만 반복하면 구하는 구간의 모든 소수가 남는다. 

#include <string>
#include <vector>
using namespace std;

int solution(int n)
{
    bool flag[100000];
    int answer = 0;
    for(int i=2;i<= n;i++){
    	flag[i] = true;
    }
    
	for(int i=2;i*i<=n;i++)
	{
		if (flag[i]){
        	for (int j=i*i;j<=n;j+=i){
            	flag[j] = false;
            }
        }
    }
    for(int i=2;i<=n;i++)
    {
    	if(flag[i]) answer++;
    }
    return answer;
}

 

728x90
반응형

+ Recent posts