no image
[Algorithm] 그리디 알고리즘(Greedy Algorithm)이란?
1. 그리디 알고리즘(Greedy Algorithm) 그리디 알고리즘은 다음과 같이 불리기도 한다. 욕심쟁이 방법 탐욕적 방법 탐욕 알고리즘 이렇게 불리는 이유는 단어의 정의 때문이다. Greedy 1. 욕심 많은 2. 탐욕적인 최적화 문제를 해결하는 알고리즘 중 하나이다. 각 단계에서 선택가능한 해들 중에서 가장 좋은 해를 찾는 문제이다. 데이터 간의 관계를 고려하지 않고 수행 과정에서 단계에 최적해인 최솟값 또는 최댓값을 가진 데이터를 선택한다.이를 '근시안적 선택'이라고 말하기도 한다. 각각의 단계에서의 선택(부분해)을 모아서 문제의 최적해를 도출한다. 2. 그리디 알고리즘의 특징 섬 A에서 섬 C를 가는 최소 경로를 구하는 문제를 통해 알아보겠다. 섬 A 에서 섬 C로 가려면 A to B, B t..
2023.11.01
[Algorithm] 알고리즘(Algorithm)이란?
1. 알고리즘(Algorithm)이란? 문제를 해결하는 단계적 절차 또는 방법이다. 입력 -> 알고리즘 -> 결과 -> 문제 해결 특성 정확성 : 알고리즘은 주어진 입력에 대해 올바른 해를 주어야 한다. 즉, 모든 입력에 대해 올바른 답을 출력해야 한다. 수행성 : 알고리즘의 각 단계는 컴퓨터에서 수행이 가능하여야 한다. 애매모호한 표현을 사용하면 안된다. 유한성 : 알고리즘은 유한 시간 내에 종료되어야 한다. 매우 오래 걸리면 알고리즘의 가치를 잃는다. 효율성 : 알고리즘은 효율적일수록 그 가치가 높아진다. 항상 시간적, 공간적인 효율성을 갖도록 고안되어야 한다. 2. 알고리즘의 표현 방법 2-1. 말로 표현된 알고리즘 첫 카드의 숫자를 읽고 기억한다. 다음 카드의 숫자를 읽고, 기억한 숫자와 비교한다..
2023.10.30
no image
[BOJ] [Python] 3187번 : 양치기 꿍
https://www.acmicpc.net/problem/3187 3187번: 양치기 꿍 입력의 첫 번째 줄에는 각각 영역의 세로와 가로의 길이를 나타내는 두 개의 정수 R, C (3 ≤ R, C ≤ 250)가 주어진다. 다음 각 R줄에는 C개의 문자가 주어지며 이들은 위에서 설명한 기호들이다. www.acmicpc.net 문제 더보기 양치기 꿍은 맨날 늑대가 나타났다고 마을 사람들을 속였지만 이젠 더이상 마을 사람들이 속지 않는다. 화가 난 꿍은 복수심에 불타 아예 늑대들을 양들이 있는 울타리안에 마구 집어넣어 양들을 잡아먹게 했다. 하지만 양들은 보통 양들이 아니다. 같은 울타리 영역 안의 양들의 숫자가 늑대의 숫자보다 더 많을 경우 늑대가 전부 잡아먹힌다. 물론 그 외의 경우는 양이 전부 잡아먹히겠..
2023.08.27
[프로그래머스] [Python] (Level 2) 할인 행사
https://school.programmers.co.kr/learn/courses/30/lessons/131127 프로그래머스 코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요. programmers.co.kr 문제 더보기 XYZ 마트는 일정한 금액을 지불하면 10일 동안 회원 자격을 부여합니다. XYZ 마트에서는 회원을 대상으로 매일 한 가지 제품을 할인하는 행사를 합니다. 할인하는 제품은 하루에 하나씩만 구매할 수 있습니다. 알뜰한 정현이는 자신이 원하는 제품과 수량이 할인하는 날짜와 10일 연속으로 일치할 경우에 맞춰서 회원가입을 하려 합니다. 예를 들어, 정현이가 원하는 제품이 바나나 3개, 사..
2023.08.06
no image
[프로그래머스] [Python] (Level 2) 우박수열 정적분
https://school.programmers.co.kr/questions/41525 프로그래머스코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.programmers.co.kr문제더보기콜라츠 추측이란 로타르 콜라츠(Lothar Collatz)가 1937년에 제기한 추측으로 모든 자연수 n에 대해 다음 작업을 반복하면 항상 1로 만들 수 있다는 추측입니다.1-1. 입력된 수가 짝수라면 2로 나눕니다.1-2. 입력된 수가 홀수라면 3을 곱하고 1을 더합니다.2.결과로 나온 수가 1보다 크다면 1번 작업을 반복합니다.예를 들어 주어진 수가 5 라면 5 ⇒ 16 ⇒ 8 ⇒ 4 ⇒2 ⇒ 1 이되어 총 5번만에..
2023.07.30
no image
[BOJ] [Python] 17069번 : 파이프 옮기기 2
https://www.acmicpc.net/problem/17069 17069번: 파이프 옮기기 2 유현이가 새 집으로 이사했다. 새 집의 크기는 N×N의 격자판으로 나타낼 수 있고, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 (r, c)로 나타낼 수 있다. 여기서 r은 행의 번호, c는 열의 www.acmicpc.net 문제 및 입출력 더보기 문제 유현이가 새 집으로 이사했다. 새 집의 크기는 N×N의 격자판으로 나타낼 수 있고, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 (r, c)로 나타낼 수 있다. 여기서 r은 행의 번호, c는 열의 번호이고, 행과 열의 번호는 1부터 시작한다. 각각의 칸은 빈 칸이거나 벽이다. 오늘은 집 수리를 위해서 파이프 하나를 옮기려고 한다. ..
2023.07.24
no image
[BOJ] [Python] 15992번 : 1, 2, 3 더하기 7
https://www.acmicpc.net/problem/15992 15992번: 1, 2, 3 더하기 7 각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다. 단, 사용한 수의 개수는 m개 이어야 한다. 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과 m이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하는 프로그램을 작성하시오. 단, 사용한 수의 개수는 m개 이어야 한다. 입출력 보기 더보기 입력 첫째 줄에 테스트 케이스의..
2023.07.13
[프로그래머스] [Python] (Level 3) 인사고과 ( + 추가 test case)
문제 더보기 완호네 회사는 연말마다 1년 간의 인사고과에 따라 인센티브를 지급합니다. 각 사원마다 근무 태도 점수와 동료 평가 점수가 기록되어 있는데 만약 어떤 사원이 다른 임의의 사원보다 두 점수가 모두 낮은 경우가 한 번이라도 있다면 그 사원은 인센티브를 받지 못합니다. 그렇지 않은 사원들에 대해서는 두 점수의 합이 높은 순으로 석차를 내어 석차에 따라 인센티브가 차등 지급됩니다. 이때, 두 점수의 합이 동일한 사원들은 동석차이며, 동석차의 수만큼 다음 석차는 건너 뜁니다. 예를 들어 점수의 합이 가장 큰 사원이 2명이라면 1등이 2명이고 2등 없이 다음 석차는 3등부터입니다. 각 사원의 근무 태도 점수와 동료 평가 점수 목록 scores이 주어졌을 때, 완호의 석차를 return 하도록 soluti..
2023.07.07
[프로그래머스] [Python] (Level 2) 혼자서 하는 틱택토 (+ 추가 test case)
https://school.programmers.co.kr/learn/courses/30/lessons/160585 프로그래머스 코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요. programmers.co.kr 문제 더보기 틱택토는 두 사람이 하는 게임으로 처음에 3x3의 빈칸으로 이루어진 게임판에 선공이 "O", 후공이 "X"를 번갈아가면서 빈칸에 표시하는 게임입니다. 가로, 세로, 대각선으로 3개가 같은 표시가 만들어지면 같은 표시를 만든 사람이 승리하고 게임이 종료되며 9칸이 모두 차서 더 이상 표시를 할 수 없는 경우에는 무승부로 게임이 종료됩니다. 할 일이 없어 한가한 머쓱이는 두 사람이 하는..
2023.07.06
no image
[프로그래머스] [Python] (Level 2) 미로 탈출
https://school.programmers.co.kr/learn/courses/30/lessons/159993 프로그래머스 코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요. programmers.co.kr 문제 1 x 1 크기의 칸들로 이루어진 직사각형 격자 형태의 미로에서 탈출하려고 합니다. 각 칸은 통로 또는 벽으로 구성되어 있으며, 벽으로 된 칸은 지나갈 수 없고 통로로 된 칸으로만 이동할 수 있습니다. 통로들 중 한 칸에는 미로를 빠져나가는 문이 있는데, 이 문은 레버를 당겨서만 열 수 있습니다. 레버 또한 통로들 중 한 칸에 있습니다. 따라서, 출발 지점에서 먼저 레버가 있는 칸으로 이동..
2023.07.03
728x90

 

1. 그리디 알고리즘(Greedy Algorithm)

그리디 알고리즘은 다음과 같이 불리기도 한다.

  • 욕심쟁이 방법
  • 탐욕적 방법
  • 탐욕 알고리즘 

이렇게 불리는 이유는 단어의 정의 때문이다.

Greedy
1. 욕심 많은
2. 탐욕적인

최적화 문제를 해결하는 알고리즘 중 하나이다. 각 단계에서 선택가능한 해들 중에서 가장 좋은 해를 찾는 문제이다. 데이터 간의 관계를 고려하지 않고 수행 과정에서 단계에 최적해인 최솟값 또는 최댓값을 가진 데이터를 선택한다.이를 '근시안적 선택'이라고 말하기도 한다.  

각각의 단계에서의 선택(부분해)을 모아서 문제의 최적해를 도출한다.

 

2. 그리디 알고리즘의 특징

섬 A에서 섬 C를 가는 최소 경로를 구하는 문제를 통해 알아보겠다.

섬 A 에서 섬 C로 가려면 A to B, B to C로 가야한다.

A to B 의 부분해, B to C 의 부분해를 구해서 문제의 최적해를 구한다.

여기서 특징을 알 수 있다. 각 단계에서 구한 답은 절대로 번복하지 않으며, 이후 단계는 이전 단계의 데이터를 통해 계산하지 않는다.(독립적)

 

다시 특징을 정리하자면

 

1. 한번 선택한 해답 (각각의 단계에서 구한 부분해)은 절대로 번복하지 않는다. 즉, 선택한 데이터를 버리고 다른 것을 취하지 않는다.

2. 알고리즘은 단순하며, 매우 제한적인 문제들만 그리디 알고리즘으로 해결가능하다.

3. 다른 알고리즘에 비해 계산시간이 매우 빠른 편이다.

 

그리디 알고리즘을 문제를 해겷하기 위해선 각각의 최적해들이 항상 최적임을 보장해야한다. 

 

3. 그리디 알고리즘과 동적 계획법의 차이

( Greedy Algorithm vs Dynamic Programming(DP))

구분 Greedy Algorithm Dynamic Programming
기본 원리 매 단계에서 지역적으로 최적인 선택을 한다.
부분해들을 모아 전체 문제를 해결한다.
부분 문제의 최적해를 찾아 전체 문제를 해결한다.
접근 방식 각 단계에서의 문제만을 해결한다. 문제를 작은 부분 문제로 나누어 해결한다.
메모리 사용  적다. 많다. ( 부분 문제의 해를 저장)
속도  빠르다. 느릴 수 있다. (부분 문제 계산 시간에 따라 다르다)
정확성 항상 최적해를 보장하지 않는다. 항상 최적해를 보장한다.
(부분문제들을 모두 고려했기 때문이다.)
장점 구현이 빠르고, 속도가 빠르다. 복잡한 문제에서도 최적해를 항상 보장한다.
단점 최적해를 항상 보장하지 않는다. 메모리 사용량이 크며 문제에 따라 구현이 복잡할 수 있다.

 

4. 그리디 알고리즘 예시 

다음은 그리디 알고리즘을 사용하는 예시이다.

4.1 동전 거스름돈 ( Coin Change)

https://giliit.tistory.com/61

4.2 최소신장 트리 ( MST, Minimum Spanning Tree)

  • Kruskal MST
  • Prim MST

4.3 최단경로 찾기 ( Shortest Path)

  • Dijkstra Shortest Path

4.4 부분 배낭 문제 ( Fractional Knapsack)

 

4.5 부분 집합 문제 ( Set Covering)

 

4.6 작업 스케줄링 ( Task Scheduling)

 

4.7 허프만 압축 ( Huffman Coding)

 

728x90

1. 알고리즘(Algorithm)이란?

문제를 해결하는 단계적 절차 또는 방법이다. 

입력 -> 알고리즘 -> 결과  -> 문제 해결

 

특성

  • 정확성 : 알고리즘은 주어진 입력에 대해 올바른 해를 주어야 한다. 즉, 모든 입력에 대해 올바른 답을 출력해야 한다. 
  • 수행성 : 알고리즘의 각 단계는 컴퓨터에서 수행이 가능하여야 한다. 애매모호한 표현을 사용하면 안된다.
  • 유한성 : 알고리즘은 유한 시간 내에 종료되어야 한다. 매우 오래 걸리면 알고리즘의 가치를 잃는다.
  • 효율성 : 알고리즘은 효율적일수록 그 가치가 높아진다. 항상 시간적, 공간적인 효율성을 갖도록 고안되어야 한다.

 

2. 알고리즘의 표현 방법

 

2-1. 말로 표현된 알고리즘

첫 카드의 숫자를 읽고 기억한다.
다음 카드의 숫자를 읽고, 기억한 숫자와 비교한다.
비교 후 큰 숫자를 기억한다.
다음에 읽을 카드가 있으면 line 2로 간다.
기억한 숫자가 최대 숫자다.

 

2-2. 의사 코드(pseudo code)로 표현된 알고리즘

max = A[0]
for i = 1 to 9
	if (A[i] > max) max = A[i]
return max

 

2-3. 플로차트(flow chart)로 표현된 알고리즘

매우 제한적으로 사용

 

3. 알고리즘의 효율성 표현

알고리즘의 수행 시간 또는 알고리즘이 수행하는 동안 사용 되는 메모리 공간의 크기로 나타낼 수 있다. 이들을 각각 시간복잡도(time complexity), 공간복잡도(space complexity)라고 한다. 일반적으로 알고리즘들을 비교할 때에는 시간복잡도가 주로 사용된다.

 

컴퓨터에서 수행된 시간을 측정할 수 있지만, 실제 측정된 시간은 여러 가지 변수가 있다.(컴퓨터의 성능, 프로그래밍의 언어, 최적화 등등) 따라서, 알고리즘이 수행하는 기본적인 연산 횟수를 입력 크기에 대한 함수로 표현한다.

 

예를 들어, 10장의 숫자 카드에서 최대 숫자를 찾을 때, 비교연산은 총 9번(n-1)이다. 따라서 시간복잡도는 (n-1)이다.

 

효율성 표현

  • 공간복잡도 (space complexity) : 극히 제한적인 문제(merge sort) 같은 문제에서나 사용됨
  • 시간복잡도 (time complexity)
    • 최악 경우 분석 (worst case analysis) : '어떤 입력이 주어지더라도 얼마 이상을 넘지 않는다'라는 표현
    • 평균 경우 분석 (average case analysis) : 입력이 확률 분포가 균등 분포 (uniform distribution)이라 가정한다. 즉, 입력이 무작위로 주어진다고 가정한다. 
    • 최선 경우 분석 (best case analysis) : 최적(optimal) 알고리즘을 고안하는데 참고 자료로 활용 즉, 이보다 성능이 우수한 알고리즘은 없다.

 

4. 복잡도의 표기

시간(또는 공간) 복잡도는 입력 크기에 대한 함수로 표기, 주로 여러 개의 항을 가지는 다항식이다. 이를 단순한 함수로 표현하기 위해 점근적 표기(asymptotic notation)를 사용한다. 이는 입력 크기 n이 무한대로 커질 때의 복잡도를 간단히 표현하기 위해 사용하는 표기법이다.

 

예를 들어보자

$ 3n^3 -15n^2+10n-18$ 을 $n^3$으로, $2n^2-8n+3$을 $n^2$으로, $4n+6$을 $n$으로 단순화시킨다. 즉, 다항식의 최고차항만을 계수 없이 취한 것이다. 이 식에 상한, 하한, 동일한 증가율과 같은 개념을 적용하여, 점근적 표기를 사용한다.

 

4.1 점근적 표기

  • $O$(Big-Oh)-표기 : 복잡도의 점근적 상한을 나타냄
  • $\Omega$(Big-Omega)-표기 : 복잡도의 점근적 하한을 나타냄
  • $\theta$(Theta)-표기 : 복잡도의 상한과 하한이 동시에 적용됨을 나타냄

 

$O$(Big-Oh)-표기

O-표기는 복잡도의 점근적 상한 이라는 것을 예제를 통해 살펴보자

$f(n) = 2n^2-8+3$ 이라면, $f(n)$ 의 $O$ -표기는 $O(n^2)$이다.

-> 단순화한 함수 $n^2$에 임의의 상수 c를 곱한 $cn^2$이 $n$이 증가함에 Ekfk $f(n)$의 상한이 된다. 단, $c>0$

    $O$-표기에는 c가 '숨겨져 있다'라고 생각해도 좋다.

 

$\Omega$(Big-Omega)-표기 

$\Omega$(Big-Omega)-표기는 점근적 하한이라는 것을 예제를 통해 살펴보자

$f(n) = 2n^2-8+3$ 이라면, $f(n)$ 의 $\Omega$ -표기는 $O(n^2)$이며 '$n$이 증가함에 따라 $2n^2-8n+3$이 $cn^2$보다 작을 수  없다는 의미이다. 상수 $c$ = 1 로 놓으면 된다. 위와 마찬가지로 최고차항만 계수 없이 취하면 된다.

 

$\theta$(Theta)-표기

$\theta$(Theta)-표기는 복잡도가 위의 2개와 같은 경우에 사용한다. 즉, '$f(n)은 $n$이 증가함에 따라 $n^2$과 동일한 증가율을 가진다

 

4.2 시간복잡도 표기

  • $O(1)$              상수 시간 (constant time) 
  • $O(logn)$         로그(대수) 시간 (logarithmic time)
  • $O(n)$              선형 시간 (linear time)
  • $O(nlogn)$       로그 선형 시간 (log-linear time)
  • $O(n^2)$          이차 시간 (quadratic time)
  • $O(n^3)$          3차 시간 (cubic time)
  • $O(2^n)$          지수 시간 (exponential time)
  • $O(n!)$            
728x90

https://www.acmicpc.net/problem/3187

 

3187번: 양치기 꿍

입력의 첫 번째 줄에는 각각 영역의 세로와 가로의 길이를 나타내는 두 개의 정수 R, C (3 ≤ R, C ≤ 250)가 주어진다. 다음 각 R줄에는 C개의 문자가 주어지며 이들은 위에서 설명한 기호들이다.

www.acmicpc.net

문제

더보기

양치기 꿍은 맨날 늑대가 나타났다고 마을 사람들을 속였지만 이젠 더이상 마을 사람들이 속지 않는다. 화가 난 꿍은 복수심에 불타 아예 늑대들을 양들이 있는 울타리안에 마구 집어넣어 양들을 잡아먹게 했다.

하지만 양들은 보통 양들이 아니다. 같은 울타리 영역 안의 양들의 숫자가 늑대의 숫자보다 더 많을 경우 늑대가 전부 잡아먹힌다. 물론 그 외의 경우는 양이 전부 잡아먹히겠지만 말이다.

꿍은 워낙 똑똑했기 때문에 이들의 결과는 이미 알고있다. 만약 빈 공간을 '.'(점)으로 나타내고 울타리를 '#', 늑대를 'v', 양을 'k'라고 나타낸다면 여러분은 몇 마리의 양과 늑대가 살아남을지 계산할 수 있겠는가?

단, 울타리로 막히지 않은 영역에는 양과 늑대가 없으며 양과 늑대는 대각선으로 이동할 수 없다.

입력

더보기

입력의 첫 번째 줄에는 각각 영역의 세로와 가로의 길이를 나타내는 두 개의 정수 R, C (3 ≤ R, C ≤ 250)가 주어진다.

다음 각 R줄에는 C개의 문자가 주어지며 이들은 위에서 설명한 기호들이다.

 

출력

더보기

살아남게 되는 양과 늑대의 수를 각각 순서대로 출력한다.

 

풀이방법

방법은 BFS로 풀었습니다.

 

 . , v, k 로 이루어진 블럭을 BFS로 찾습니다. 블럭안에 v와 k의 크기의 따라  k  > v 면 k, 0 을 반환하고 k <= v 면 0, v 를 반환하여 총합을 반환합니다.

 

 

코드

from collections import deque
import sys

input = sys.stdin.readline

r, c = map(int , input().rstrip().split())

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
def bfs(i, j):
    w, s = 0, 0
    visited[i][j] = True
    deq = deque([(i,j)])
    while deq:
        x, y = deq.popleft()
        if matrix[x][y] == 'k':
            s += 1
        if matrix[x][y] == 'v':
            w += 1
        for k in range(4):
            nx = x + dx[k]
            ny = y + dy[k]
            if 0 <= nx < r and 0<= ny < c and visited[nx][ny] == False and matrix[nx][ny] != '#':
                deq.append((nx,ny))
                visited[nx][ny] = True
    if s > w:
        w = 0
    else:
        s = 0
    return w, s



matrix = [list(list(input().rstrip())) for _ in range(r)]
visited = [[False] * c for _ in range(r)]

total_w, total_s = 0, 0
for i in range(r):
    for j in range(c):
        if matrix[i][j] != '#' and visited[i][j] == False:
            w,s = bfs(i,j)
            total_w += w
            total_s += s
print(total_s, total_w)
728x90

https://school.programmers.co.kr/learn/courses/30/lessons/131127

 

프로그래머스

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

programmers.co.kr

문제


더보기

XYZ 마트는 일정한 금액을 지불하면 10일 동안 회원 자격을 부여합니다. XYZ 마트에서는 회원을 대상으로 매일 한 가지 제품을 할인하는 행사를 합니다. 할인하는 제품은 하루에 하나씩만 구매할 수 있습니다. 알뜰한 정현이는 자신이 원하는 제품과 수량이 할인하는 날짜와 10일 연속으로 일치할 경우에 맞춰서 회원가입을 하려 합니다.

예를 들어, 정현이가 원하는 제품이 바나나 3개, 사과 2개, 쌀 2개, 돼지고기 2개, 냄비 1개이며, XYZ 마트에서 15일간 회원을 대상으로 할인하는 제품이 날짜 순서대로 치킨, 사과, 사과, 바나나, 쌀, 사과, 돼지고기, 바나나, 돼지고기, 쌀, 냄비, 바나나, 사과, 바나나인 경우에 대해 알아봅시다. 첫째 날부터 열흘 간에는 냄비가 할인하지 않기 때문에 첫째 날에는 회원가입을 하지 않습니다. 둘째 날부터 열흘 간에는 바나나를 원하는 만큼 할인구매할 수 없기 때문에 둘째 날에도 회원가입을 하지 않습니다. 셋째 날, 넷째 날, 다섯째 날부터 각각 열흘은 원하는 제품과 수량이 일치하기 때문에 셋 중 하루에 회원가입을 하려 합니다.

정현이가 원하는 제품을 나타내는 문자열 배열 want와 정현이가 원하는 제품의 수량을 나타내는 정수 배열 number, XYZ 마트에서 할인하는 제품을 나타내는 문자열 배열 discount가 주어졌을 때, 회원등록시 정현이가 원하는 제품을 모두 할인 받을 수 있는 회원등록 날짜의 총 일수를 return 하는 solution 함수를 완성하시오. 가능한 날이 없으면 0을 return 합니다.

 

입출력( + 추가적인 Test Case)


더보기
want number discount result
["chicken", "apple", "apple", "banana", "rice", "apple", "pork", "banana", "pork", "rice", "pot", "banana", "apple", "banana"] [3, 2, 2, 2,1] ["chicken", "apple", "apple", "banana", "rice", "apple", "pork", "banana", "pork", "rice", "pot", "banana", "apple", "banana"] 3
["apple"] [10] ["banana", "banana", "banana", "banana", "banana", "banana", "banana", "banana", "banana", "banana"] 6

제한사항


더보기
  • 1 ≤ want의 길이 = number의 길이 ≤ 10
    • 1 ≤ number의 원소 ≤ 10
    • number[i]는 want[i]의 수량을 의미하며, number의 원소의 합은 10입니다.
  • 10 ≤ discount의 길이 ≤ 100,000
  • want와 discount의 원소들은 알파벳 소문자로 이루어진 문자열입니다.
    • 1 ≤ want의 원소의 길이, discount의 원소의 길이 ≤ 12

풀이방법


유저의 쇼핑목록과 개수를 Counter class 로 구성합니다.

1일차부터 n, len(discount)-9 일까지의 각각의 카운터를 구성합니다.

만약 user_counter 와 mart_counter가 동일하다면 answer 을 1씩 더합니다.

마지막 answer을 반환합니다.

 

코드


from collections import Counter

def solution(want, number, discount):
    answer = 0
    user = dict()
    for i in zip(want,number):
        user[i[0]] = i[1]
    user_counter = Counter(user)
    for i in range(0,len(discount)-sum(number)+1):
        mart = Counter(discount[i:i+10])
        if user_counter == mart:
            answer += 1
    return answer
728x90

https://school.programmers.co.kr/questions/41525

 

프로그래머스

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

programmers.co.kr

문제


더보기

콜라츠 추측이란 로타르 콜라츠(Lothar Collatz)가 1937년에 제기한 추측으로 모든 자연수 n에 대해 다음 작업을 반복하면 항상 1로 만들 수 있다는 추측입니다.

1-1. 입력된 수가 짝수라면 2로 나눕니다.
1-2. 입력된 수가 홀수라면 3을 곱하고 1을 더합니다.
2.결과로 나온 수가 1보다 크다면 1번 작업을 반복합니다.

예를 들어 주어진 수가 5 라면 5 ⇒ 16 ⇒ 8 ⇒ 4 ⇒2 ⇒ 1 이되어 총 5번만에 1이 됩니다.

수가 커졌다 작아지기를 반복하는 모습이 비구름에서 빗방울이 오르락내리락하며 우박이 되는 모습과 비슷하다고 하여 우박수 또는 우박수열로 불리기도 합니다. 현재 이 추측이 참인지 거짓인지 증명되지 않았지만 약 1해까지의 수에서 반례가 없음이 밝혀져 있습니다.

은지는 우박수열을 좌표 평면 위에 꺾은선 그래프로 나타내보려고 합니다. 초항이 K인 우박수열이 있다면, x = 0일때 y = K이고 다음 우박수는 x = 1에 표시합니다. 이런 식으로 우박수가 1이 될 때까지 점들을 찍고 인접한 점들끼리 직선으로 연결하면 다음과 같이 꺾은선 그래프를 만들 수 있습니다.

은지는 이렇게 만든 꺾은선 그래프를 정적분 해보고 싶어졌습니다. x에 대한 어떤 범위 [a, b]가 주어진다면 이 범위에 대한 정적분 결과는 꺾은선 그래프와 x = a, x = b, y = 0 으로 둘러 쌓인 공간의 면적과 같습니다. 은지는 이것을 우박수열 정적분이라고 정의하였고 다양한 구간에 대해서 우박수열 정적분을 해보려고 합니다.

단, 우박수열 그래프의 가로축 길이를 미리 알 수 없기 때문에 구간의 시작은 음이 아닌 정수, 구간의 끝은 양이 아닌 정수로 표현합니다. 이는 각각 꺾은선 그래프가 시작하는 점과 끝나는 점의 x좌표에 대한 상대적인 오프셋을 의미합니다.

예를 들어, 5를 초항으로 하는 우박수열은 5 ⇒ 16 ⇒ 8 ⇒ 4 ⇒ 2 ⇒ 1 입니다. 이를 좌표 평면으로 옮기면 (0, 5), (1, 16), (2, 8), (3, 4), (4, 2), (5, 1) 에 점이 찍히고 점들을 연결하면 꺾은선 그래프가 나옵니다. 이를 [0,0] 구간에 대해 정적분 한다면 전체 구간에 대한 정적분이며, [1,-2] 구간에 대해 정적분 한다면 1 ≤ x ≤ 3인 구간에 대한 정적분입니다.

우박수의 초항 k와, 정적분을 구하는 구간들의 목록 ranges가 주어졌을 때 정적분의 결과 목록을 return 하도록 solution을 완성해주세요. 단, 주어진 구간의 시작점이 끝점보다 커서 유효하지 않은 구간이 주어질 수 있으며 이때의 정적분 결과는 -1로 정의합니다.

 

입출력( + 추가적인 Test Case)


k ranges result
5 [[0,0],[0,-1],[2,-3],[3,-3]] [33.0,31.5,0.0,-1.0]
5 [[8,0]] [-1.0]

제한사항


더보기

 

  • 2 ≤ k ≤ 10,000
  • 1 ≤ ranges의 길이 ≤ 10,000
    • ranges의 원소는 [a, b] 형식이며 0 ≤ a < 200, -200 < b ≤ 0 입니다.
  • 주어진 모든 입력에 대해 정적분의 결과는 227 을 넘지 않습니다.
  • 본 문제는 정답에 실수형이 포함되는 문제입니다. 입출력 예의 소수 부분 .0이 코드 실행 버튼 클릭 후 나타나는 결괏값, 기댓값 표시와 다를 수 있습니다.

풀이방법


1. k에 대한 우박수열을 구합니다.

2. 각각의 구간에 대해 넓이를 구합니다. ex) 0 ~ 1 구간, 1 ~ 2 구간 등등등

3. ranges 를 index 형식으로 변환합니다.(k 수열의 길이가 7인 경우, [0, 0] -> [0, 7])

3-1.  변환 후 뒤의 숫자가 앞의 숫자보다 작은 경우 -1.0 을 반환합니다. ex) [6,3]

4. 각각의 구간의 합을 구한 뒤 반환합니다.

 

코드


def solution(k, ranges):
	
    # 우박수열
    k_list = [k]
    while k != 1:
        if k % 2 == 0:
            k = k// 2
            k_list.append(k)
        else:
            k = k * 3 + 1
            k_list.append(k)

	# 각각의 구간의 넓이
    integral = []
    for i in range(len(k_list)-1):
        temp = (k_list[i]+k_list[i+1])/2
        integral.append(temp)

	# index 변환 및 범위 판단 후 넓이를 list에 추가
    answer = []
    for i in ranges:
        a,b = i[0], len(k_list)+i[1]-1
        if b < a:
            answer.append(-1.0)
        else:
            answer.append(float(sum(integral[a:b])))
    return answer

 

728x90

https://www.acmicpc.net/problem/17069

 

17069번: 파이프 옮기기 2

유현이가 새 집으로 이사했다. 새 집의 크기는 N×N의 격자판으로 나타낼 수 있고, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 (r, c)로 나타낼 수 있다. 여기서 r은 행의 번호, c는 열의

www.acmicpc.net

문제 및 입출력 

 

더보기

문제

유현이가 새 집으로 이사했다. 새 집의 크기는 N×N의 격자판으로 나타낼 수 있고, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 (r, c)로 나타낼 수 있다. 여기서 r은 행의 번호, c는 열의 번호이고, 행과 열의 번호는 1부터 시작한다. 각각의 칸은 빈 칸이거나 벽이다.

오늘은 집 수리를 위해서 파이프 하나를 옮기려고 한다. 파이프는 아래와 같은 형태이고, 2개의 연속된 칸을 차지하는 크기이다.

 

파이프는 회전시킬 수 있으며, 아래와 같이 3가지 방향이 가능하다.

 

파이프는 매우 무겁기 때문에, 유현이는 파이프를 밀어서 이동시키려고 한다. 벽에는 새로운 벽지를 발랐기 때문에, 파이프가 벽을 긁으면 안 된다. 즉, 파이프는 항상 빈 칸만 차지해야 한다.

파이프를 밀 수 있는 방향은 총 3가지가 있으며, →, ↘, ↓ 방향이다. 파이프는 밀면서 회전시킬 수 있다. 회전은 45도만 회전시킬 수 있으며, 미는 방향은 오른쪽, 아래, 또는 오른쪽 아래 대각선 방향이어야 한다.

파이프가 가로로 놓여진 경우에 가능한 이동 방법은 총 2가지, 세로로 놓여진 경우에는 2가지, 대각선 방향으로 놓여진 경우에는 3가지가 있다.

아래 그림은 파이프가 놓여진 방향에 따라서 이동할 수 있는 방법을 모두 나타낸 것이고, 꼭 빈 칸이어야 하는 곳은 색으로 표시되어져 있다.

가장 처음에 파이프는 (1, 1)와 (1, 2)를 차지하고 있고, 방향은 가로이다. 파이프의 한쪽 끝을 (N, N)로 이동시키는 방법의 개수를 구해보자.

입력

첫째 줄에 집의 크기 N(3 ≤ N ≤ 32)이 주어진다. 둘째 줄부터 N개의 줄에는 집의 상태가 주어진다. 빈 칸은 0, 벽은 1로 주어진다. (1, 1)과 (1, 2)는 항상 빈 칸이다.

 

출력

첫째 줄에 파이프의 한쪽 끝을 (N, N)으로 이동시키는 방법의 수를 출력한다. 이동시킬 수 없는 경우에는 0을 출력한다.

 

풀이방법

 

처음의 파이프는 이와 같이 가로로 되어 있습니다. 

입력받은 N x N 의 DP행렬을 생성합니다. 각각의 행렬안에는 크기가 3인 배열이 있으며 각각 대각선, 세로, 가로입니다.

처음 파이프가 가로로 놓아져있을 때의 초기값입니다.

* 입력받은 행렬을 matrix로 표현합니다.

이후 DP[i][j]의 칸의 값을 구하는 방법은 다음과 같습니다.

 

(대각선)

matrix[i-1][j-1], matrix[i-1][j], matrix[i][j-1]의 칸이 모두 0인 경우 (모두 벽이 아닌 빈칸)

DP[i][j][0] = sum(DP[i-1][j-1])

 

(세로)

matrix[i-1][j-1] 이 0인 경우(벽이 아닌 빈칸)

현재칸의 세로 파이프 = 위칸에서의 대각선으로 들어온 파이프 + 위칸에서의 세로로 들어온 파이프

dp[i][j][1] = dp[i-1][j][0] + dp[i-1][j][1]

 

(가로)

matrix[i-1][j-1] 이 0인 경우(벽이 아닌 빈칸)

현재칸의 가로 파이프 = 왼쪽칸에서의 대각선으로 들어온 파이프 + 왼쪽칸에서의 가로로 들어온 파이프

dp[i][j][2] = dp[i][j-1][0] + dp[i][j-1][2]

 

 

코드

import sys

# 입력
N = int(input())
matrix = [list(map(int,sys.stdin.readline().rstrip().split())) for _ in range(N)]
# 대각선, 세로, 가로
dp = [[[0] * 3 for _ in range(N+1)] for __ in range(N+1)]
dp[1][2][2] = 1
for i in range(1,N+1):
    for j in range(3, N+1):
        if matrix[i-1][j-1] == 0:
            # 대각선
            if matrix[i-1][j-2] == 0 and matrix[i-2][j-1] == 0:
                dp[i][j][0] = sum(dp[i-1][j-1])
            # 세로
            dp[i][j][1] = dp[i-1][j][0] + dp[i-1][j][1]
            # 가로
            dp[i][j][2] = dp[i][j-1][0] + dp[i][j-1][2]

print(sum(dp[N][N]))

결론

처음의 초기값을 잘 주는 것을 성공적으로해서 생각보다 빨리 풀 수 있었습니다.

마지막 DP배열을 증가시키는 과정에서 다른 방법을 쓰면 DP배열을 확장하지 않아도 될 것 같지만 코드의 가독성을 위해 썼습니다.

 

728x90

https://www.acmicpc.net/problem/15992

 

15992번: 1, 2, 3 더하기 7

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다. 단, 사용한 수의 개수는 m개 이어야 한다.

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과 m이 주어졌을 때, n을 1, 2, 3의 합으로 나타내는 방법의 수를 구하는 프로그램을 작성하시오. 단, 사용한 수의 개수는 m개 이어야 한다.

 

입출력 보기

더보기

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고, 정수 n과 m이 주어진다. n은 양수이며 1,000보다 작거나 같다. m도 양수이며, n보다 작거나 같다.

 

출력

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다. 단, 사용한 수의 개수는 m개 이어야 한다.

 

풀이방법

m = 수의 합, n = 사용한 수의 개수

왼쪽의 표를 보게 되면 형광색으로 칠한 것의 합이 빨간색 화살표로 가리킨 숫자와 같습니다.

그 이유는 오른쪽의 한 예시를 보면 알 수 있습니다. 

예시) f(4,2)에서  1 + 3, 2 + 2, 3 + 1 이렇게 3개의 숫자입니다. 이 숫자들은 각각 1, 2, 3에 3, 2, 1를 더한 것입니다.

이 예시를 통해 정리한 식은 우하단의 식과 같습니다.

f(m, n) = f(m-1, n-1) + f(m-2, n-1) + f(m-3, n-1) -> 각각의 수들에서 1, 2, 3을 더한 수들의 합이라고 보시면 됩니다. 

 

 

 

코드

import sys

input=sys.stdin.readline
MAX = 1001
MOD = 1_000_000_009

# 초기값 설정
dp = [[0] * MAX for _ in range(MAX)]
dp[1][1] = 1
dp[2][1], dp[2][2] = 1,1
dp[3][1], dp[3][2], dp[3][3] = 1, 2, 1

for i in range(4, MAX):
    for j in range(1, i + 1):
        dp[i][j] = (dp[i-1][j-1] + dp[i-2][j-1] + dp[i-3][j-1]) % MOD

# 출력
N = int(input())
for i in range(N):
    n, m = map(int, input().split())
    print(dp[n][m])

'Algorithm > BOJ' 카테고리의 다른 글

[BOJ] [Python] 3187번 : 양치기 꿍  (0) 2023.08.27
[BOJ] [Python] 17069번 : 파이프 옮기기 2  (0) 2023.07.24
[BOJ] [Python] 15828번 : Router  (0) 2023.06.24
[BOJ] [Python] 2164번 : 카드2  (0) 2023.06.23
[BOJ] [Python] 10845번 큐  (0) 2023.06.23
728x90

문제


더보기

완호네 회사는 연말마다 1년 간의 인사고과에 따라 인센티브를 지급합니다. 각 사원마다 근무 태도 점수와 동료 평가 점수가 기록되어 있는데 만약 어떤 사원이 다른 임의의 사원보다 두 점수가 모두 낮은 경우가 한 번이라도 있다면 그 사원은 인센티브를 받지 못합니다. 그렇지 않은 사원들에 대해서는 두 점수의 합이 높은 순으로 석차를 내어 석차에 따라 인센티브가 차등 지급됩니다. 이때, 두 점수의 합이 동일한 사원들은 동석차이며, 동석차의 수만큼 다음 석차는 건너 뜁니다. 예를 들어 점수의 합이 가장 큰 사원이 2명이라면 1등이 2명이고 2등 없이 다음 석차는 3등부터입니다.

각 사원의 근무 태도 점수와 동료 평가 점수 목록 scores이 주어졌을 때, 완호의 석차를 return 하도록 solution 함수를 완성해주세요.

 

입출력( + 추가적인 Test Case)


scores result
[[2,2],[1,4],[3,2],[3,2],[2,1]] 4
[[4,3],[5,5],[4,4],[2,2],[6,0]] -1
[[9,0],[5,5],[6,6]] 2

제한사항


더보기
  • 1 ≤ scores의 길이 ≤ 100,000
  • scores의 각 행은 한 사원의 근무 태도 점수와 동료 평가 점수를 나타내며 [a, b] 형태입니다.
    • scores[0]은 완호의 점수입니다.
    • 0 ≤ a, b ≤ 100,000
  • 완호가 인센티브를 받지 못하는 경우 -1을 return 합니다.

풀이방법


1. 완호의 점수를 저장해놓습니다.

2. 점수를 근무 태도 점수는 내림차순, 동료 평가 점수는 오름차순으로 정렬합니다.

3. 현재 점수가 완호의 점수보다 모두 크다면 -1을 반환합니다.

4. limit > score[1]인 경우, incentive를 받지 못하는 사람이므로 다음 차례로 넘어갑니다.

5. limit <= score[1]인 경우, incentive를 받는 사람이므로 완호의 점수의 총합과 비교해서 등수를 계산합니다.

6. limit을 갱신합니다.

7. 비교가 끝나면 등수를 반환합니다.

 

* limit > score[1]일 때, incentive를 받지 못하는 이유에 대해 설명해보겠습니다.

limit은 이전 수들 중에서 가장 큰 동료 평가 점수(score[1])입니다.

현재 동료 평가 점수(socre[1])가 limit 보다 작다면, 동료의 인사고과 점수보다 낮은 것을 알 수 있습니다.

현재 근무 태도 점수(socre[0])은 내림차순입니다. 다시 말한다면, 이전 동료의 근무 태도 점수가 더 높다는 것입니다.

다시말해서, score[0], score[1] 모두 동료의 점수보다 낮다는 것을 알 수 있습니다.

 

 

 

 

코드


def solution(scores):
    answer = 1
    wanho_score = scores[0]
    sum_wanho_score = sum(scores[0])
    scores.sort(key=lambda x: (-x[0], x[1]))
    limit = 0

    for score in scores:
        sum_score = sum(score)
        if wanho_score[0] < score[0] and wanho_score[1] < score[1]:
            return -1

        # limit > score[1] 은 incentive를 받지 못하는 사람이다.
        if limit <= score[1]:

            # 성립한다는 것은 인센티브를 받는 것이므로 숫자를 계산해서 등수를 계산한다.
            if sum_wanho_score < sum_score:
                answer += 1
            limit = score[1]
    return answer

 

결론


처음에 문제를 잘못 해석해 시간이 오래 걸렸다. 10번 줄에 max(wanho_score) < min(score)인 경우로 계산했는데 이 것은 틀린 풀이이다. 두 값 중 min과 max를 구하는 것이 아닌 각각의 요소의 대소관계를 계산했어야 했다. 그 이후 오름차순과 내림차순을 떠올리지 못해서 다른 분들의 코드를 참고해서 문제를 풀었다. Level 3으로 와서 그런지 문제가 상당히 어렵다.

728x90

https://school.programmers.co.kr/learn/courses/30/lessons/160585

 

프로그래머스

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

programmers.co.kr

 

문제


더보기

틱택토는 두 사람이 하는 게임으로 처음에 3x3의 빈칸으로 이루어진 게임판에 선공이 "O", 후공이 "X"를 번갈아가면서 빈칸에 표시하는 게임입니다. 가로, 세로, 대각선으로 3개가 같은 표시가 만들어지면 같은 표시를 만든 사람이 승리하고 게임이 종료되며 9칸이 모두 차서 더 이상 표시를 할 수 없는 경우에는 무승부로 게임이 종료됩니다.

할 일이 없어 한가한 머쓱이는 두 사람이 하는 게임인 틱택토를 다음과 같이 혼자서 하려고 합니다.

  • 혼자서 선공과 후공을 둘 다 맡는다.
  • 틱택토 게임을 시작한 후 "O"와 "X"를 혼자서 번갈아 가면서 표시를 하면서 진행한다.

틱택토는 단순한 규칙으로 게임이 금방 끝나기에 머쓱이는 한 게임이 종료되면 다시 3x3 빈칸을 그린 뒤 다시 게임을 반복했습니다. 그렇게 틱택토 수 십 판을 했더니 머쓱이는 게임 도중에 다음과 같이 규칙을 어기는 실수를 했을 수도 있습니다.

  • "O"를 표시할 차례인데 "X"를 표시하거나 반대로 "X"를 표시할 차례인데 "O"를 표시한다.
  • 선공이나 후공이 승리해서 게임이 종료되었음에도 그 게임을 진행한다.

게임 도중 게임판을 본 어느 순간 머쓱이는 본인이 실수를 했는지 의문이 생겼습니다. 혼자서 틱택토를 했기에 게임하는 과정을 지켜본 사람이 없어 이를 알 수는 없습니다. 그러나 게임판만 봤을 때 실제로 틱택토 규칙을 지켜서 진행했을 때 나올 수 있는 상황인지는 판단할 수 있을 것 같고 문제가 없다면 게임을 이어서 하려고 합니다.

머쓱이가 혼자서 게임을 진행하다 의문이 생긴 틱택토 게임판의 정보를 담고 있는 문자열 배열 board가 매개변수로 주어질 때, 이 게임판이 규칙을 지켜서 틱택토를 진행했을 때 나올 수 있는 게임 상황이면 1을 아니라면 0을 return 하는 solution 함수를 작성해 주세요.

 

입출력( + 추가적인 Test Case)


board result
["O.X", ".O.", "..X"] 1
["OOO", "...", "XXX"] 0
["...", ".X.", "..."] 0
["...", "...", "..."] 1
["OXX",".OX","..O"] 1
["OXX","OXX","OOO"] 0
["O.X","OOX",".OX"] 0

제한사항


더보기
  • board의 길이 = board[i]의 길이 = 3
    • board의 원소는 모두 "O", "X", "."으로만 이루어져 있습니다.
  • board[i][j]는 i + 1행 j + 1열에 해당하는 칸의 상태를 나타냅니다.
    • "."은 빈칸을, "O"와 "X"는 해당 문자로 칸이 표시되어 있다는 의미입니다.

풀이방법


틱택토가 성립이 안되는 경우가 있습니다.

1. 순서가 어긋난 경우

2. X가 성립되고 O를 놓는 경우

3. O가 성립되고 X를 놓는 경우

총 3가지가 있습니다.

 

1번의 경우

O의 개수는 cnt_o, X의 개수는 cnt_X 입니다.

cnt_x > cnt_o 의 경우

cnt_o > cnt_x + 1 의 경우 2가지 중 하나라도 성립되는 경우 오류입니다.

 

tic_o는 O가 성립된 경우, tic_x는 X가 성립된 경우 입니다.

 

2번의 경우

tic_x == 1 이면서 cnt_o > cnt_x 이면, X가 성립되고 O를 놓은 경우이므로 오류입니다.

 

3번의 경우

tic_o == 1 이면서 cnt_o == cnt_x 이면, O가 성립되고, X를 놓는 경우이므로 오류입니다.

 

총 3가지의 경우를 거치고 오류가 없다면 틱택토는 성립합니다.

코드


def tic_tac_toe(player, board):
    tic = 0

    for i in range(3):
        if all(player == cell for cell in board[i]):
            tic += 1

    for j in range(3):
        if all(player == board[i][j] for i in range(3)):
            tic += 1

    if all(board[i][i] == player for i in range(3)):
        tic += 1
    if all(board[i][2 - i] == player for i in range(3)):
        tic += 1

    return tic


def solution(board):
    board_list = [list(i) for i in board]

    # x, o 개수 세기
    cnt_x, cnt_o = 0, 0
    for i in range(3):
        for j in range(3):
            if board_list[i][j] == 'X':
                cnt_x += 1
            elif board_list[i][j] == 'O':
                cnt_o += 1
            else:
                continue

    # 순서가 어긋난 경우
    if cnt_x > cnt_o or cnt_o > cnt_x + 1:
        return 0
        
	# x와 o의 틱택토 성립하는지 확인
    tic_o = tic_tac_toe('O', board)
    tic_x = tic_tac_toe('X', board)

    # o 가 완성된 이후 x를 놓은 경우
    if tic_o == 1 and (cnt_x == cnt_o):
        return 0

    # x 가 완성된 이후 o를 놓은 경우
    if tic_x == 1 and (cnt_o > cnt_x):
        return 0

    return 1

 

all functions을 사용하지 않은 경우

더보기
def tic_tac_toe(board_list):
    tic_o = 0
    if board_list[0] == ['O', 'O', 'O'] or board_list[1] == ['O', 'O', 'O'] or board_list[2] == ['O', 'O', 'O']:
        tic_o += 1

    if board_list[0][0] == 'O' and board_list[1][0] == 'O' and board_list[2][0] == 'O':
        tic_o += 1
    elif board_list[0][1] == 'O' and board_list[1][1] == 'O' and board_list[2][1] == 'O':
        tic_o += 1
    elif board_list[0][2] == 'O' and board_list[1][2] == 'O' and board_list[2][2] == 'O':
        tic_o += 1

    if board_list[0][0] == 'O' and board_list[1][1] == 'O' and board_list[2][2] == 'O':
        tic_o += 1
    elif board_list[0][2] == 'O' and board_list[1][1] == 'O' and board_list[2][0] == 'O':
        tic_o += 1

    tic_x = 0
    if board_list[0] == ['X', 'X', 'X'] or board_list[1] == ['X', 'X', 'X'] or board_list[2] == ['X', 'X', 'X']:
        tic_x += 1

    if board_list[0][0] == 'X' and board_list[1][0] == 'X' and board_list[2][0] == 'X':
        tic_x += 1
    elif board_list[0][1] == 'X' and board_list[1][1] == 'X' and board_list[2][1] == 'X':
        tic_x += 1
    elif board_list[0][2] == 'X' and board_list[1][2] == 'X' and board_list[2][2] == 'X':
        tic_x += 1

    if board_list[0][0] == 'X' and board_list[1][1] == 'X' and board_list[2][2] == 'X':
        tic_x += 1
    elif board_list[0][2] == 'X' and board_list[1][1] == 'X' and board_list[2][0] == 'X':
        tic_x += 1

    return tic_o, tic_x


def solution(board):
    board_list = [list(i) for i in board]

    # x, o 개수 세기
    cnt_x, cnt_o = 0, 0
    for i in range(3):
        for j in range(3):
            if board_list[i][j] == 'X':
                cnt_x += 1
            elif board_list[i][j] == 'O':
                cnt_o += 1
            else:
                continue

    # 순서가 어긋난 경우
    if cnt_x > cnt_o or cnt_o > cnt_x + 1:
        return 0

    tic_o, tic_x = tic_tac_toe(board_list)

    # o 가 완성되었는데 x를 놓은 경우
    if tic_o == 1 and (cnt_x == cnt_o):
        return 0

    # x 가 완성되었는데 o를 놓은 경우
    if tic_x == 1 and (cnt_o > cnt_x):
        return 0

    answer = 1
    return answer

상당히 코드가 더럽다. all function을 사용해서 깔끔하게 바꾸었다. 

 

결론


처음에 규칙을 찾느라 어려웠다. 틱택토를 해본적도 없었기 때문이다. 규칙을 빨리 찾고나서 구현은 생각보다 쉬웠다. 하지만 밑에 있는 코드는 너무 복잡해 보여서 Python의 all function을 사용해서 깔끔하게 바꾸었다. 덕분에 all function을 배워갔다.

728x90

https://school.programmers.co.kr/learn/courses/30/lessons/159993

 

프로그래머스

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

programmers.co.kr

문제


1 x 1 크기의 칸들로 이루어진 직사각형 격자 형태의 미로에서 탈출하려고 합니다. 각 칸은 통로 또는 벽으로 구성되어 있으며, 벽으로 된 칸은 지나갈 수 없고 통로로 된 칸으로만 이동할 수 있습니다. 통로들 중 한 칸에는 미로를 빠져나가는 문이 있는데, 이 문은 레버를 당겨서만 열 수 있습니다. 레버 또한 통로들 중 한 칸에 있습니다. 따라서, 출발 지점에서 먼저 레버가 있는 칸으로 이동하여 레버를 당긴 후 미로를 빠져나가는 문이 있는 칸으로 이동하면 됩니다. 이때 아직 레버를 당기지 않았더라도 출구가 있는 칸을 지나갈 수 있습니다. 미로에서 한 칸을 이동하는데 1초가 걸린다고 할 때, 최대한 빠르게 미로를 빠져나가는데 걸리는 시간을 구하려 합니다.

미로를 나타낸 문자열 배열 maps가 매개변수로 주어질 때, 미로를 탈출하는데 필요한 최소 시간을 return 하는 solution 함수를 완성해주세요. 만약, 탈출할 수 없다면 -1을 return 해주세요.

 

입출력( + 추가적인 Test Case)


maps result
["SOOOL","XXXXO","OOOOO","OXXXX","OOOOE"] 16
["LOOXS","OOOOX","OOOOO","OOOOO","EOOOO"] -1

제한사항에 대해 보고싶으면 눌러주세요

더보기

제한사항


  • 5 ≤ maps의 길이 ≤ 100
    • 5 ≤ maps[i]의 길이 ≤ 100
    • maps[i]는 다음 5개의 문자들로만 이루어져 있습니다.
      • S : 시작 지점
      • E : 출구
      • L : 레버
      • O : 통로
      • X : 벽
    • 시작 지점과 출구, 레버는 항상 다른 곳에 존재하며 한 개씩만 존재합니다.
    • 출구는 레버가 당겨지지 않아도 지나갈 수 있으며, 모든 통로, 출구, 레버, 시작점은 여러 번 지나갈 수 있습니다.

풀이방법


사용한 알고리즘 : BFS

 

크게 2개로 나누어서 구현했습니다.

1. S 에서 L까지의 최단거리를 구합니다.

2. L 에서 E까지의 최단거리를 구합니다.

-> 최단거리를 구하는 방식은 BFS를 사용해서 풀었습니다.

 

코드


from collections import deque

INF = float('inf')

def solution(maps):
    answer = 0
    n = len(maps)
    m = len(maps[0])
    matrix = [[INF] * m for _ in range(n)]
    deq = deque()
    
    # S 위치 찾기
    for i in range(n):
        for j in range(m):
            if maps[i][j] == 'S':
                matrix[i][j] = 0
                deq.append((i, j, 0))
                break
        if deq:
            break

    dx = [0, 0, -1, 1]
    dy = [-1, 1, 0, 0]

    # S => L
    while deq:
        x, y, cnt = deq.popleft()

        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]

            if 0 <= nx < n and 0 <= ny < m and maps[nx][ny] != 'X':
            
            	# L을 찾았을 경우
                if maps[nx][ny] == 'L':
                    answer += cnt + 1

                    # deq 초기화
                    deq = deque()
                    deq.append((nx, ny, 0))
                    break
                    
                if cnt + 1 < matrix[nx][ny]:
                    matrix[nx][ny] = cnt + 1
                    deq.append((nx, ny, cnt + 1))
        if answer != 0:
            break

    temp_answer = answer
    matrix = [[INF] * m for _ in range(n)]

    # L => E
    
    while deq:
        x, y, cnt = deq.popleft()

        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]

            if 0 <= nx < n and 0 <= ny < m and maps[nx][ny] != 'X':
                if maps[nx][ny] == 'E':
                    answer += cnt + 1
                    break
                if cnt + 1 < matrix[nx][ny]:
                    matrix[nx][ny] = cnt + 1
                    deq.append((nx, ny, cnt + 1))
        
        # answer 값이 변하면 break
        if answer != temp_answer:
            break

	# L -> E 경로가 없으면 -1
    if answer == temp_answer:
        return -1

    return answer

 

결론


1. S 에서 L까지의 최단거리를 구합니다.

2. L 에서 E까지의 최단거리를 구합니다.

 

1에서 최단거리가 없을 경우, 2에서 최단거리가 없는 경우와 둘 다 없는 경우를 생각해서 풀면 바로 풀리는 문제이다. 단, 범위오류가 발생할 수 있으니 변수 설정을 잘 해주면 된다.

 

처음 겪어보는 런타임 에러여서 당황했지만 백준에서 해왔던 것처럼 범위 오류라 가정하고 변수를 재설정하여 해결하였다.