728x90
반응형

www.acmicpc.net/problem/1463

 

1463번: 1로 만들기

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.

www.acmicpc.net

문제

정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.

  1. X가 3으로 나누어 떨어지면, 3으로 나눈다.
  2. X가 2로 나누어 떨어지면, 2로 나눈다.
  3. 1을 뺀다.

정수 N이 주어졌을 때, 위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최솟값을 출력하시오.

입력

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.

출력

첫째 줄에 연산을 하는 횟수의 최솟값을 출력한다.

예제 입력 1

2

예제 출력 1

1

예제 입력 2

10

예제 출력 2

3

 

[문제 해설]

 1로 만들기는 다이나믹 프로그래밍의 대표적인 예이다. 1로 만드는 3가지 방법은 3으로 나누기, 2로 나누기, 1 빼기 이다. 어떤 것이 연산을 더 적게 할 수 있는지 생각하면 된다. 

 

 먼저 d 배열은 만든다. d 배열은 각각의 input n에서 최소의 값을 저장한 배열이다. n은 1보다 크기 때문에 반복문은 2부터 n까지 돌린다. 먼저 d[i]는 d[i-1] +1은 1빼는 것이므로 먼저 저장해두고 비교한다. 비교할 것은 3으로 나누는 것과 2로 나누는 것을 비교해주면 된다. 

 

3으로 나누어 떨어진다고 하면 d[i] = d[i//3] + 1 이다. 1을 더해주는 것은 3으로 나누는 연산을 해주었기 때문이다. 그럼 1을 빼는 것이 연산을 더 줄일 수 있는지 3으로 나누는 것이 더 줄일 수 있는지 구한다. 이 식이 바로

d[i]=min(d[i//3]+1, d[i])

 이다. 2로 나누는 것도 똑같이 해주면 된다.

# 1로 만들기
import sys
input = sys.stdin.readline

n = int(input())

d = [0]*1000002

for i in range(2,n+1):
    d[i] = d[i - 1] + 1
    if i%3==0:
        d[i]=min(d[i//3]+1, d[i])
    if i%2==0:
        d[i]=min(d[i//2]+1, d[i])

print(d[n])
728x90
반응형
728x90
반응형

www.acmicpc.net/problem/2579

 

2579번: 계단 오르기

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점

www.acmicpc.net

문제

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점수를 얻게 된다.

예를 들어 <그림 2>와 같이 시작점에서부터 첫 번째, 두 번째, 네 번째, 여섯 번째 계단을 밟아 도착점에 도달하면 총 점수는 10 + 20 + 25 + 20 = 75점이 된다.

계단 오르는 데는 다음과 같은 규칙이 있다.

  1. 계단은 한 번에 한 계단씩 또는 두 계단씩 오를 수 있다. 즉, 한 계단을 밟으면서 이어서 다음 계단이나, 다음 다음 계단으로 오를 수 있다.
  2. 연속된 세 개의 계단을 모두 밟아서는 안 된다. 단, 시작점은 계단에 포함되지 않는다.
  3. 마지막 도착 계단은 반드시 밟아야 한다.

따라서 첫 번째 계단을 밟고 이어 두 번째 계단이나, 세 번째 계단으로 오를 수 있다. 하지만, 첫 번째 계단을 밟고 이어 네 번째 계단으로 올라가거나, 첫 번째, 두 번째, 세 번째 계단을 연속해서 모두 밟을 수는 없다.

각 계단에 쓰여 있는 점수가 주어질 때 이 게임에서 얻을 수 있는 총 점수의 최댓값을 구하는 프로그램을 작성하시오.

입력

입력의 첫째 줄에 계단의 개수가 주어진다.

둘째 줄부터 한 줄에 하나씩 제일 아래에 놓인 계단부터 순서대로 각 계단에 쓰여 있는 점수가 주어진다. 계단의 개수는 300이하의 자연수이고, 계단에 쓰여 있는 점수는 10,000이하의 자연수이다.

출력

첫째 줄에 계단 오르기 게임에서 얻을 수 있는 총 점수의 최댓값을 출력한다.

예제 입력 1 복사

6 10 20 15 25 10 20

예제 출력 1 복사

75

 

[문제 해설]

일단.acmicpc.net/problem/2579

 

2579번: 계단 오르기

 

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점

 

www.acmicpc.net

문제

계단 오르기 게임은 계단 아래 시작점부터 계단 꼭대기에 위치한 도착점까지 가는 게임이다. <그림 1>과 같이 각각의 계단에는 일정한 점수가 쓰여 있는데 계단을 밟으면 그 계단에 쓰여 있는 점수를 얻게 된다.

 

예를 들어 <그림 2>와 같이 시작점에서부터 첫 번째, 두 번째, 네 번째, 여섯 번째 계단을 밟아 도착점에 도달하면 총 점수는 10 + 20 + 25 + 20 = 75점이 된다.

 

계단 오르는 데는 다음과 같은 규칙이 있다.

 

계단은 한 번에 한 계단씩 또는 두 계단씩 오를 수 있다. 즉, 한 계단을 밟으면서 이어서 다음 계단이나, 다음 다음 계단으로 오를 수 있다.

연속된 세 개의 계단을 모두 밟아서는 안 된다. 단, 시작점은 계단에 포함되지 않는다.

마지막 도착 계단은 반드시 밟아야 한다.

따라서 첫 번째 계단을 밟고 이어 두 번째 계단이나, 세 번째 계단으로 오를 수 있다. 하지만, 첫 번째 계단을 밟고 이어 네 번째 계단으로 올라가거나, 첫 번째, 두 번째, 세 번째 계단을 연속해서 모두 밟을 수는 없다.

 

각 계단에 쓰여 있는 점수가 주어질 때 이 게임에서 얻을 수 있는 총 점수의 최댓값을 구하는 프로그램을 작성하시오.

 

입력

입력의 첫째 줄에 계단의 개수가 주어진다.

 

둘째 줄부터 한 줄에 하나씩 제일 아래에 놓인 계단부터 순서대로 각 계단에 쓰여 있는 점수가 주어진다. 계단의 개수는 300이하의 자연수이고, 계단에 쓰여 있는 점수는 10,000이하의 자연수이다.

 

출력

첫째 줄에 계단 오르기 게임에서 얻을 수 있는 총 점수의 최댓값을 출력한다.

 

예제 입력 1 복사

6 10 20 15 25 10 20

 

예제 출력 1 복사

75

 

[문제 해설]

 일단 최대값을 구해야 된다는 사실을 알 수 있다. max()를 쓸 생각을 하고 있자.

계단을 한 번에 한 계단씩 또는 두 계단씩 오를 수 있다. 그래서 생각할 수 있는게

r[i]의 경우에 r[i-2]에 마지막 계단을 밟는 경우와 r[i-1]의 경우에서 마지막 계단을 밟는 경우 2개 중 어느게 최대값인지 계산하면 된다고 생각했다.

 근데, 연속된 세 개의 계단을 모두 밟아서는 안된다는 조건이 있다. r[i-1]의 경우에서 마지막 계단을 밟는 경우가 연속된 세 개의 계단을 밟은것인지 아닌지 확신할 수 없다.

 그래서 r[i-3]까지 생각해야된다. 그래서 r[i]는 r[i-2]+d[i] (여기서 d[i]는 i번째 계단의 점수)와 r[i-3]+ d[i-1] + d[i] 중 최대값을 구하면 된다.

 그리고 초기값 n이 0일때는 0, n이 1일 때는 계단 첫번째 값 (d[0]), n이 2일 때는 d[0]+d[1]이 된다. 

 

n = int(input())
d = []
r = [0] * n
for _ in range(n):
    d.append(int(input()))

if n==0:
    print(0)
elif n==1:
    print(d[0])
elif n==2:
    print(d[0]+d[1])
else:
    r[0] = d[0]
    r[1] = d[0] + d[1]
    r[2] = max(d[0] + d[2], d[1] + d[2])

    for i in range(3, n):
        r[i] = max(r[i - 2] + d[i], r[i - 3] + d[i - 1] + d[i])

    print(r[n - 1])

 

728x90
반응형
728x90
반응형

www.acmicpc.net/problem/9095

 

9095번: 1, 2, 3 더하기

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.

www.acmicpc.net

문제

2×n 크기의 직사각형을 1×2, 2×1 타일로 채우는 방법의 수를 구하는 프로그램을 작성하시오.

아래 그림은 2×5 크기의 직사각형을 채운 한 가지 방법의 예이다.

입력

첫째 줄에 n이 주어진다. (1 ≤ n ≤ 1,000)

출력

첫째 줄에 2×n 크기의 직사각형을 채우는 방법의 수를 10,007로 나눈 나머지를 출력한다.

예제 입력 1 복사

2

예제 출력 1 복사

2

예제 입력 2 복사

9

예제 출력 2 복사

55

 

[문제 풀이]

다이나믹 프로그래밍의 대표적인 문제이다. 2 x n의 타일링은 일단 가로가 관건이다. 

타일은 1x2, 2x1 밖에 없다. n길이의 가로를 채울려면 일단 n-2길이를 채우고 1x2타일을 2개 넣으면된다.

또, n-1길이를 채우고 2x1타일을 채우면 된다.

따라서 다이나믹 프로그래밍에 따라 점화식을 세워보자.

d[i]=d[i-2]+d[i-1]

그리고 초기값을 구하면 된다. d[1]일 때는 2x1밖에 안들어간다. d[1]=1

d[2]는 2x1 타일 2개 아니면 1x2 타일 2개를 쓰면 채워진다. d[2] = 2

 

초기값을 채우고 나서 나머지 식을 구하면 된다.

#2Xn 타일링

n = int(input())

d = [0]*1001
d[1]=1
d[2]=2
for i in range(3,n+1):
    d[i]=d[i-2]+d[i-1]

result = d[n]%10007
print(result)
728x90
반응형
728x90
반응형

www.acmicpc.net/problem/2839

 

2839번: 설탕 배달

상근이는 요즘 설탕공장에서 설탕을 배달하고 있다. 상근이는 지금 사탕가게에 설탕을 정확하게 N킬로그램을 배달해야 한다. 설탕공장에서 만드는 설탕은 봉지에 담겨져 있다. 봉지는 3킬로그

www.acmicpc.net

문제

상근이는 요즘 설탕공장에서 설탕을 배달하고 있다. 상근이는 지금 사탕가게에 설탕을 정확하게 N킬로그램을 배달해야 한다. 설탕공장에서 만드는 설탕은 봉지에 담겨져 있다. 봉지는 3킬로그램 봉지와 5킬로그램 봉지가 있다.

상근이는 귀찮기 때문에, 최대한 적은 봉지를 들고 가려고 한다. 예를 들어, 18킬로그램 설탕을 배달해야 할 때, 3킬로그램 봉지 6개를 가져가도 되지만, 5킬로그램 3개와 3킬로그램 1개를 배달하면, 더 적은 개수의 봉지를 배달할 수 있다.

상근이가 설탕을 정확하게 N킬로그램 배달해야 할 때, 봉지 몇 개를 가져가면 되는지 그 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다. (3 ≤ N ≤ 5000)

출력

상근이가 배달하는 봉지의 최소 개수를 출력한다. 만약, 정확하게 N킬로그램을 만들 수 없다면 -1을 출력한다.

[풀이]

 일단 d배열의 전체를 10001로 저장해두고 d[0]=0을 해둔다. 보텀업으로 아래부터 하나하나씩 채워가기 위해서 둔것이다. 예를 들어 n을 18을 입력하였다면 18에서 5를 뺀 값은 13이다. 그럼 d[13]이 저장되어있다면 d[18]과 d[13]+1 중에서 최소값이 어떤 것인지 찾아내면 되는 것이다. 5를 뺀 d[13]이 10001이면(그러니까 5kg 3kg 두 개로 배달이 불가능한 경우일때)는 d[18]에서 3을 뺀 d[15]가 있는지 한번 살펴보는 것이다. 그럼 또 d[15]+1과 d[18]의 최소값을 찾으면 되는것이다. 예전에 '효율적인 화폐 구성' 문제에서 아이디어를 받았다. (N가지 종류의 화폐로 비용을 지불하는 문제)

 

n = int(input())

d = [10001]*5001
d[0]=0
for i in range(3,n+1):
    if i-5>=0 and d[i-5]!=10001:
        d[i]=min(d[i],d[i-5]+1)
    if d[i-3]!=10001:
        d[i]=min(d[i],d[i-3]+1)

if d[n]==10001:
    print(-1)
else:
    print(d[n])
728x90
반응형
728x90
반응형

www.acmicpc.net/problem/9095

 

9095번: 1, 2, 3 더하기

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.

www.acmicpc.net

문제

정수 4를 1, 2, 3의 합으로 나타내는 방법은 총 7가지가 있다. 합을 나타낼 때는 수를 1개 이상 사용해야 한다.

  • 1+1+1+1
  • 1+1+2
  • 1+2+1
  • 2+1+1
  • 2+2
  • 1+3
  • 3+1

정수 n이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 정수 n이 주어진다. n은 양수이며 11보다 작다.

출력

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 출력한다.

예제 입력 1

3 4 7 10

예제 출력 1

7 44 274

 

[풀이]

 먼저, 숫자가 3개 1,2,3이 있다는 사실을 안다. 4부터는 4하나로는 만들 수가 없다. 배열 d에 d[1], d[2], d[3]에 대한 값을 저장해두고 d[1], d[2], d[3]을 이용해서 나머지 4부터 10까지의 배열값을 저장할려고 했다.

 

4는 1에 3을 더한것, 2에 2를 더한것, 3에 1을 더한것이므로 d[4] = d[1] + d[2] + d[3] 이라고 생각할 수 있다.

5는 1에 4를 더한것, 2에 3을 더한것, 3에 2를 더한것, 4에 1을 더한것이라고 생각하면 되는데 1,2,3밖에 없으므로

d[5] = d[4] + d[3] + d[2] 까지만 하면 된다.

 

d[i] = d[i-1] + d[i-2] + d[i-3]이라는 점화식을 끌어오면 된다.

 

#1,2,3 더하기

T = int(input())
d=[0]*11
d[1]=1
d[2]=2
d[3]=4

for i in range(4,11):
    d[i]=d[i-3]+d[i-2]+d[i-1]
for i in range(T):
    n = int(input())
    print(d[n])

 

728x90
반응형
728x90
반응형

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

 

코딩테스트 연습 - 체육복

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번�

programmers.co.kr

[문제 설명]

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.

전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.

 

[제한사항]

  • 전체 학생의 수는 2명 이상 30명 이하입니다.
  • 체육복을 도난당한 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌의 체육복을 가져온 학생의 수는 1명 이상 n명 이하이고 중복되는 번호는 없습니다.
  • 여벌 체육복이 있는 학생만 다른 학생에게 체육복을 빌려줄 수 있습니다.
  • 여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.

[입출력 예]

n lost reserve return
5 [2, 4] [1, 3, 5] 5
5 [2, 4] [3] 4
3 [3] [1] 2

[입출력 예 설명]

 

예제 #1
1번 학생이 2번 학생에게 체육복을 빌려주고, 3번 학생이나 5번 학생이 4번 학생에게 체육복을 빌려주면 학생 5명이 체육수업을 들을 수 있습니다.

예제 #2
3번 학생이 2번 학생이나 4번 학생에게 체육복을 빌려주면 학생 4명이 체육수업을 들을 수 있습니다.

 

[풀이 과정]

[풀이 1] -> [풀이 2]((시행착오)) -> [풀이 3]

 

[그리디 알고리즘](https://jobdong7757.tistory.com/6) : 이 문제는 전체적으로 최적의 해가 있는 문제가 아니다. 여벌의 체육복이 있고, 앞이나 뒤에 도둑맞은 사람이 있다면 체육복을 그 때 그때 상황에 따라서 나누어 주어야 되기 때문에 부분적으로 최적인 해가 전체적으로 최적인 해가 아닌 경우가 생기게 된다.
 

[풀이 1] : 3, 7, 12번 오류

 먼저 lost.size()만큼 반복문을 실행한다. temp라는 변수에 lost[i]-1을 저장해서 lost의 왼쪽의 수가 reserve에 있는지 확인해 주는 것이다. <algorithm>헤더에 있는 find()함수를 이용하여서 reserve 배열에 있다면 lost를 0으로 바꾸어준다. 그리고 lost[i]+1을 if - else구문으로 확인해준다. 

 마지막에는 lost에 0이 아니면 여벌의 체육복을 받지 못한것이므로 count에 lost가 0이 아닌 것들의 수를 파악해주고 n에서 빼주었다. 

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int solution(int n, vector<int> lost, vector<int> reserve) {
    int answer;
    for(int i=0;i<lost.size();i++){
        int temp = lost[i]-1;
        if(lost[i]!=0){
            if(find(reserve.begin(),reserve.end(),temp)!=reserve.end()){
                lost[i]=0;
                int index = find(reserve.begin(),reserve.end(),temp)-reserve.begin();
                reserve.erase(reserve.begin()+index);
            }
            else{
                int temp = lost[i]+1;
                if(find(reserve.begin(),reserve.end(),temp)!=reserve.end()){
                    lost[i]=0;
                    int index = find(reserve.begin(),reserve.end(),temp)-reserve.begin();
                    reserve.erase(reserve.begin()+index);
                }
            }
        }
    }
    int count = 0;
    for(int i=0;i<lost.size();i++){
        if(lost[i]!=0) count++;
    }
    answer = n - count;
    return answer;
}

3번, 7번, 12번에서 오류가 났다. 제한사항의 마지막인 '여벌 체육복을 가져온 학생이 체육복을 도난당했을 수 있습니다. 이때 이 학생은 체육복을 하나만 도난당했다고 가정하며, 남은 체육복이 하나이기에 다른 학생에게는 체육복을 빌려줄 수 없습니다.' 이 부분의 테스트 케이스에서 오류가 난 것 같다. 

 

[풀이 2] : 테스트 7 오류

[풀이 2]에서는 [풀이 1] 앞에 반복문을 통해 lost와 reserve가 같은, 즉 제한사항의 마지막 번을 확인해 주었다. 같은 것이 있다면 lost와 reserve 양쪽 모두 해당하는 것을 제거해주었다.

#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int solution(int n, vector<int> lost, vector<int> reserve) {
    int answer;
    for(int i=0;i<lost.size();i++){
        if(find(reserve.begin(),reserve.end(),lost[i])!=reserve.end()){
            int index = find(reserve.begin(),reserve.end(),lost[i])-reserve.begin();
            reserve.erase(reserve.begin()+index);
            lost.erase(lost.begin()+i);
        }
    }
    for(int i=0;i<lost.size();i++){
        int temp = lost[i]-1;
        if(lost[i]!=0){
            if(find(reserve.begin(),reserve.end(),temp)!=reserve.end()){
                lost[i]=0;
                int index = find(reserve.begin(),reserve.end(),temp)-reserve.begin();
                reserve.erase(reserve.begin()+index);
            }
            else{
                int temp = lost[i]+1;
                if(find(reserve.begin(),reserve.end(),temp)!=reserve.end()){
                    lost[i]=0;
                    int index = find(reserve.begin(),reserve.end(),temp)-reserve.begin();
                    reserve.erase(reserve.begin()+index);
                }
            }
        }
    }
    int count = 0;
    for(int i=0;i<lost.size();i++){
        if(lost[i]!=0) count++;
    }
    answer = n - count;
    return answer;
}

아직까지 7번 테스트 케이스에서 오류가 났다.. 

 

[풀이 3]

 중복제거에서 오류가 나는 것같아서 새로운 배열을 통해 해결하였다. 새로운 int 배열 student를 만들어서 n개의 원소를 1로 초기화시킨다. lost에 해당하는 원소가 student에 있으면 빼주고, reserve에 있으면 더해준다. 그래서 2보다 크면 왼쪽먼저 확인해서 0이면 주고, 그 뒤에 오른쪽을 확인한다. 

#include <string>
#include <vector>

using namespace std;

int solution(int n, vector<int> lost, vector<int> reserve) {
    int answer = 0;
    vector<int> student(n,1);
    
    for(int i=0;i<lost.size();i++){
        student[lost[i]-1]--;
    }
    
    for(int i=0;i<reserve.size();i++){
        student[reserve[i]-1]++;
    }
    
    for(int i=0;i<student.size();i++){
        if(student[i]==2){
            if(i!=0){
                if(student[i-1]==0) {
                    student[i]--;
                    student[i-1]++;
                }
            }
        }
        if(student[i]==2){
            if(i+1<student.size()){
                if(student[i+1]==0){
                    student[i+1]++;
                    student[i]--;
                }
            }
        }
    }
    for(int i=0;i<student.size();i++){
        if(student[i]!=0) answer++;
    }
    return answer;
}
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