반응형

나머진 다 쉬웠는데, 가장 가까운 경로에 있는 애를 어떻게 먹으러갈지 구현하기 힘들었던 문제.

우선순위가

1. 자기보다 크기가 작은 물고기가 2마리 이상인 경우 가장 가까이에 있는 것

2. 거리가 같은 경우에는 행과 열이 작을 수록.

 

이건데 1번을 건너뛰고 자꾸 2번 우선순위로 가가지고 힘들었음.

다른 골3에 비해 얘가 좀 더 어려웠던 느낌

import sys
from collections import deque
sys.stdin = open('111.txt')

input = sys.stdin.readline

N = int(input().rstrip())
graph = []
for col in range(N):
    graph.append(list(map(int,input().rstrip().split())))
    for row in range(N):
        if graph[col][row] == 9:
            sy,sx = col,row # 상어 위치 저장
            graph[col][row] = 0
    

q = deque()


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

# 물고기 찾으러 감
def search():
    visited = [[-1]*N for _ in range(N)]
    visited[sy][sx] = 0 # 상어 시작 위치
    q.append((sy,sx))
    
    while q:
        y,x = q.popleft()
        for i in range(4):
            ny = y+dy[i]
            nx = x+dx[i]
            if 0<=ny<N and 0<=nx<N:
                if visited[ny][nx] == -1:
                    if -1<=graph[ny][nx] <= shark_size:
                        visited[ny][nx] = visited[y][x]+1
                        q.append((ny,nx))
                        
    return visited


def eating(visited):
    min_check = int(1e9)
    for col in range(N):
        for row in range(N):
            if 1<=graph[col][row] < shark_size:
                if visited[col][row] != -1:
                    if visited[col][row] < min_check:
                        min_check = visited[col][row]
                        sy,sx = col,row
                        
    if min_check == int(1e9):
        return False # 더 이상 먹을게 없다는 뜻
    
    return sy,sx,min_check

time = 0
eat_cnt = 0 # 물고기 먹은 횟수
while True:
    visited = search()    
    result = eating(visited)
    
    if result == False:
        print(time)
        break
    
    sy,sx = result[0],result[1] # 상어 재 시작 위치
    time += result[2]
    graph[sy][sx] = 0
    eat_cnt += 1
    
    if eat_cnt >= shark_size:
        shark_size+=1
        eat_cnt = 0
반응형
반응형

회전하는 방식에서 상당히 애를 먹음. 이 부분만 잘 처리하면 나머진 기본적인 플러드필이다.

# N은 격자 사이즈, Q는 레벨 횟수
import copy
from collections import deque
N,Q = map(int,input().split())

_N = 2**N # 제곱 수라했으니까.

ice = []
for col in range(_N):
    ice.append(list(map(int,input().split())))

level = list(map(int,input().split()))

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

### 위에 입력 이상 없음 ###
for idx in level:
    k = 2**idx # 레벨 단위로 해야하니까
    for col in range(0,_N,k):
        for row in range(0,_N,k):
            temp = []
            for small_col in range(col,col+k):
                temp.append(ice[small_col][row:row+k])

            for small_col in range(k):
                for small_row in range(k):
                    # 시계방향 회전
                    ice[col+small_row][row+k-small_col-1] = temp[small_col][small_row]

    
    ice_check = [[0]*_N for _ in range(_N)]
    for col in range(_N):
        for row in range(_N):
            cnt = 0
            for i in range(4):
                ny = col+dy[i]
                nx = row+dx[i]
                if 0<=ny<_N and 0<=nx<_N and ice[ny][nx]>0:
                    cnt +=1

            ice_check[col][row] = cnt

    for col in range(_N):
        for row in range(_N):
            if ice_check[col][row] < 3: # 인접 얼음이 3개 미만이면
                if ice[col][row] > 0: # 그리고 그 중심에 얼음이 있다면
                    ice[col][row] -= 1


### 위에꺼 다 끝내고 나면!
def bfs(y,x):
    global ccnt
    q = deque()
    q.append((y,x))

    while q:
        y,x = q.popleft()
        for i in range(4):
            ny = y+dy[i]
            nx = x+dx[i]
            if 0<=ny<_N and 0<=nx<_N:
                if not visited[ny][nx] and ice[ny][nx] > 0:
                    visited[ny][nx] = True
                    ccnt+=1
                    q.append((ny,nx))

    return ccnt


arr = []
visited = [[False]*_N for _ in range(_N)]
ccnt = 0
for col in range(_N):
    for row in range(_N):
        if ice[col][row] > 0 and not visited[col][row]:
            visited[col][row] = True
            ccnt = 1
            result = bfs(col,row)
            arr.append(result)

tot = 0
for col in range(_N):
    for row in range(_N):
        tot += ice[col][row]

if tot!=0:
    print(tot)
    print(max(arr))
    
else:
    print(tot)
    print(0)
반응형
반응형

하라는 대로만 하면 되는 문제다. 시간이 빡빡해보였는데 한번에 통과해서 기분 좋다.

이 문제에서는 주의해야할 점은..... 죽은 나무를 언제 처리할 것인지 생각하는게 관건이다.

clear와 extend 함수 스킬을 알고 있어서 다행이었다.

# N은 격자 사이즈, M은 바이러스 개수, K는 사이클 횟수
N,M,K = map(int,input().split())

# 초기 배양액
init_eat = [[5]*N for _ in range(N)]

# 사이클 1회 마치고 추가될 양분
add_eat = []
for col in range(N):
    add_eat.append(list(map(int,input().split())))

# 바이러스 정보들
virus = [[[] for _ in range(N)] for _ in range(N)]
for col in range(M):
    y,x,age = map(int,input().split())
    virus[y-1][x-1].append(age)

# 방향 키
step = [(1,0),(0,1),(-1,0),(0,-1),(1,1),(-1,-1),(1,-1),(-1,1)]

#### 입력 이상 X ####

# 1번/2번 행동 시작
def eating():
    for col in range(N):
        for row in range(N):
            if virus[col][row]: # 바이러스가 있는 곳이라면
                virus[col][row].sort() # 나이가 어린 바이러스부터 먹어야하므로,
                temp = []
                dead = 0
                for age in virus[col][row]:
                    if age <= init_eat[col][row]: # 양분이 나이보다 크거나 같을 경우
                        init_eat[col][row] -= age
                        age+=1
                        temp.append(age)

                    else:
                        dead += age//2 # 현재 나이의 절반만큼의 양분

                init_eat[col][row] += dead
                virus[col][row].clear() # 한번 싹 치워주고
                virus[col][row].extend(temp)


# 3번 행동 시작
def grow():
    for col in range(N):
        for row in range(N):
            if virus[col][row]: # 바이러스가 있다면
                for age in virus[col][row]:
                    if age%5==0:
                        for idx in step:
                            ny = col+idx[0]
                            nx = row+idx[1]
                            if 0<=ny<N and 0<=nx<N:
                                virus[ny][nx].append(1) # 나이가 1인 나무 추가

for _ in range(K):
    eating()
    grow()
    for col in range(N):
        for row in range(N):
            init_eat[col][row] += add_eat[col][row]

tot = 0
for col in range(N):
    for row in range(N):
        if virus[col][row]:
            tot += len(virus[col][row])

print(tot)
반응형
반응형

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

 

15686번: 치킨 배달

크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸

www.acmicpc.net

백트랙킹을 사용하는 문제입니다.

import sys
sys.setrecursionlimit(10**8)
sys.stdin = open('111.txt')

N,M = map(int,input().split()) # N은 격자 사이즈, M은 치킨집 개수
graph = []
house = []
chicken = []


for col in range(N):
    graph.append(list(map(int,input().split())))
    for row in range(N):
        if graph[col][row] == 1:
            house.append((col,row))
            
        elif graph[col][row] == 2:
            chicken.append((col,row))

기본적으로 먼저 짜둬야 하는 코드들입니다.

그래프 정보를 받을 때, 집과 치킨집의 좌표를 따로 받아줍니다.

 

이제 백트랙킹을 이용해서 각 집마다 치킨집과의 거리를 다 구해볼게요.

2차원에서 각 좌표들끼리의 조합을 구하는 방법은 거의 공식처럼 쓰입니다.

혹시, 이 문제를 풀기 전에 백준에서 N과M 시리즈를 푸셨다면 1차원에서 조합을 짜는 법은

손에 익으셨을텐데요. 2차원에선 막막할겁니다.

 

안그렇다구요? 재능러시군요.

 

저는 일단 이 문제를 처음 봤을 때, combinations을 쓰면 풀 수는 있지만 재귀를 사용해서 풀고자 했습니다. 근데 아이디어가 안 떠올라서 모 블로그를 참고해서 구현하는 방법을 참고했어요. 알고보니 공식처럼 쓰이더라구요.

 

2차원에서 좌표를 조합하는 방법부터 먼저 짚고 가겠습니다. 금방합니다!

arr = []
def back(num,cnt):
    if num>len(chicken):
        return
    
    if cnt==M:
        for idx in arr:
            cx,cy = chicken[idx]
            print((cx,cy),end=" ")
        return

    arr.append(num)
    back(num+1,cnt+1)
    arr.pop()
    back(num+1,cnt)

(back(0,0))

자, 이게 공식처럼 쓰이는 2차원 백트랙킹 조합 코드입니다.

 

아래 테케를 기준으로 위 백트랙킹 코드로 치킨집 좌표들을 뽑아보면요.

5 2
0 2 0 1 0
1 0 1 0 0
0 0 0 0 0
2 0 0 1 1
2 2 0 1 2

구분이 잘 안가있지만

(0,1) , (3,0)

(0,1) , (4,0)

(0,1) , (4,1)

.

.

.

이런 식으로 좌표가 스윽 나옵니다.

여기서 num > len(chicken)의 역할은 치킨 리스트에 담은 좌표들의 개수보다 넘어가면 인덱스에러가 뜨기때문에, 그걸 잡아주는 역할을 합니다. cnt는 최대 몇개의 좌표를 뽑을지 보는 것이라 cnt==M이 되면 이제 좌표 쌍을 뽑도록 합니다.

 

위 코드는 그냥 공식처럼 받아들이시면 됩니다.

 

그렇게 치킨 좌표들 다 뽑았으면 이제 집이랑 같이 이중 포문 돌려서 각각의 거리들을의 모든 합을 다 구해주면 됩니다.

그걸 min함수를 이용해서 계속 갱신해주면 되겠습니다.

 

아래는 해답코드이며 복붙해가실 때는 sys.stdin = open 이부분 주석처리해주세요~

import sys
sys.setrecursionlimit(10**8)
sys.stdin = open('111.txt')

N,M = map(int,input().split()) # N은 격자 사이즈, M은 치킨집 개수
graph = []
house = []
chicken = []


for col in range(N):
    graph.append(list(map(int,input().split())))
    for row in range(N):
        if graph[col][row] == 1:
            house.append((col,row))
            
        elif graph[col][row] == 2:
            chicken.append((col,row))


arr = []
real_check = int(1e9)
def back(num,cnt):
    global real_check
    if num>len(chicken):
        return
    
    if cnt==M:
        result_tot = 0
        for hx,hy in house:
            min_check = int(1e9)
            for idx in arr:
                cx,cy = chicken[idx]
                min_check = min(min_check,abs(hx-cx)+abs(hy-cy))
                
            result_tot+=min_check
            
        real_check = min(result_tot,real_check)
        return

    arr.append(num)
    back(num+1,cnt+1)
    arr.pop()
    back(num+1,cnt)
    return real_check

print(back(0,0))
반응형
반응형

PROJECTION : 컬럼명을 지정해서 데이터를 가져오는 쿼리문

SELECT NAME FROM TABLE
	ORDER BY SAL LIMIT 1

위 코드의 경우는 테이블에서 NAME컬럼만 출력한다. 근데 출력할 때는 SAL(연봉)을 기준으로 해서 가장 제일 위에 뜨는거 하나만 출력하는 형태이다.

 

SELECTION : 전체 혹은 원하는 컬럼만을 가져오는 것

* 표시나 WHERE과 같다

 

SELECT관련된 문제를 하나 풀이 해보았다.

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

 

프로그래머스

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

programmers.co.kr

해당 문제는 주어진 테이블에서 2021년에 가입한 회원 중, 나이가 20~29 사이인 회원의 수를 카운팅하는 것이다.

SELECT COUNT(AGE) AS USERS FROM USER_INFO
    WHERE year(JOINED) = '2021' AND AGE>=20 AND AGE<=29

조건은 2가지를 줬다. 2021년에 가입했으며 20대인 회원이다. 따라서 WHERE절을 사용했다.

SQL에서 쿼리의 실행순서는

FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT 이다.

즉, 이 순서대로 해석하자면  USER_INFO라는 테이블에서(FROM)  가입연도가 2021년이고, 나이는 20~29살 사이 (WHERE) 인 회원들 출력하는데 USERS라는 별칭으로 출력해라 (SELECT) 라는 것이다.

 

COUNT(칼럼명)은  해당 칼럼의 데이터 수를 세라는 것이고, 뒤에 AS는 기존 칼럼명에서 다른 칼럼명으로 바꾼다는 의미다. 따라서 AGE -> USERS로 바꾼 것이다.

 

문제를 하나 더 풀자면

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

 

프로그래머스

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

programmers.co.kr

SELECT BOOK_ID, DATE_FORMAT(PUBLISHED_DATE,'%Y-%m-%d') AS PUBLISHED_DATE FROM BOOK
    WHERE year(PUBLISHED_DATE) = '2021' and CATEGORY = '인문'
    ORDER BY PUBLISHED_DATE

문제는 BOOK이라는 테이블에서  조건 1) 2021년에 출판된  조건 2) 인문 카테고리에 속하는 도서 리스트를 찾아서

도서ID와 출판일로 출력하라는 SQL문을 작성해달라고 한다. 결과는 출판일 기준으로 오름차순 정렬이다.

 

살짝 까다로울 수도 있는데 아직 어려운 수준은 아니다.

조건들은 WHERE에 넣어주면 되는데 DATE_FORMAT이란게 있다. 기본적으로  날짜함수는

Y,M,D뿐만 아니라 뒤에 시-분-초까지도 다 나온다. 근데 문제에서 요구하는건 시,분,초는 생략하는 것이므로

DATE_FORMAT이라는 함수를 사용했다. 이를 사용하는 방법은 위와 같다.

 

별칭은 잊지말자.

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

 

프로그래머스

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

programmers.co.kr

SELECT FLAVOR FROM FIRST_HALF
    ORDER BY TOTAL_ORDER DESC , SHIPMENT_ID ASC

SELECT문의 마지막 문제로 가져왔다.

이 문제는 TOTAL_ORDER를 내림차순으로 하되, 같은 값을 가질 땐 SHIPMENT_ID를 기준으로 오름차순시키라는 문제다. 다른 언어와 달리 따로 조건문을 쓸 필요없이 위 코드처럼 짜놓으면 된다.

반응형
반응형

SCSA 교육 입과 전, Python 기초 문법을 예습해가는 시간을 갖고자 합니다.

 

먼저 산술연산자 4개부터 봅시다. 당연한 사칙연산들인데요.  C/C++에선 + , - , / , %이 있습니다.

파이썬에서는 특이한게 +,-,/,//,%,divmod(a,b) 로 크게 6가지가 있어요.

 

+ 는 더하기 ,  - 빼기 , / 는 소수점자리까지 출력하는 나누기  , // 는 몫만 출력 (C/C++에선 주석처리였죠?) , % 는 나머지 출력 ,  divmod(a,b)는  a//b , a%b를 동시에 출력하는 문법입니다.

 

 

다음은 문자열입니다. C/C++에선  문자는 작은따옴표로 , 문자열은 큰따옴표로 구분했는데 파이썬은 상관없습니다.

작은따옴표로 하든 큰따옴표로 하든  알아서 다 인식이 됩니다.

a = 'abc'

b = 'def'   이렇게 있을 때, 문자열 연산은

print(a+b)의 결과는 abcdef가 됩니다.  그냥 앞의 문자열과 뒤의 문자열이 합쳐지는 것입니다!

 

 

다음은 리스트입니다. 튜플/딕셔너리는 잠시 접어두고  리스트만 보고 다음 글로 넘어갈게요.

리스트는 C/C++에서 배열과 동일합니다. 리스트를 비워두고 입력받는 방법은 지금은 말고 리스트와 관련된 문법만

리뷰해보겠습니다.

 

hello = ['1','2','3','4','5']  이렇게 리스트가 있다합시다.

근데, 생각해보니 우리는 배열(리스트)의 인덱스가 0부터 시작한다는 사실을 까먹고 있었습니다. 그래서 가장 뒤 원소를 지우고 제일 앞에 0을 추가하고자 합니다.

 

이럴 때 사용되는 리스트 문법은 2가지가 있습니다.

 

1. remove

위 리스트에서 원소 5를 빼고자할때는 hello.remove('5')를 해주면 삭제가 되구요.

 

2. insert

hello.insert(0,'0')를 하면 됩니다.  앞에 0은 인덱스를 가리키고, 뒤에 '0'은 0번째 인덱스에 박아넣어라! 라는 말입니다.

반응형
반응형

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

 

2751번: 수 정렬하기 2

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 수가 주어진다. 이 수는 절댓값이 1,000,000보다 작거나 같은 정수이다. 수는 중복되지 않는다.

www.acmicpc.net

해당 문제는 수의 개수가 100만까지 있다. N^2로 하면 시간초과가 날게 뻔하다. 정렬 알고리즘 중에서 최대 NlogN까지의 시간복잡도를 갖는 애로 가야한다.

 

1) 퀵소트로 풀어봤다.

#include <stdio.h>

void quick (int arr[], int start, int end)
{
	if (start>=end)
    {
    	return;
    }
    
    int key = start;
    int i = start+1;
    int j = end;
    int change;
    
    while (i<=j)
    {
    	while (arr[key]<=arr[i])
        {
        	i++;
        }
        
        while (arr[j]>arr[key] && j>key)
        {
        	j--;
        }
        
        if (i>j)
        {
        	change = arr[j];
            arr[j] = arr[key];
            arr[key] = change;
        }
        
        else
        {
        	change = arr[j];
            arr[j] = arr[i];
            arr[i] = change;
        }
    }
    
    quick (arr,start,j-1);
    quick (arr,j+1,end);
}

int main()
{
	int i,number = 0;
	scanf("%d",&number);
	int arr[number];
	for (i=0; i<number; i++)
	{
		scanf("%d",&arr[i]);
	}
	quick (arr, 0 , number-1);
	for (i=0; i<number; i++)
	{
		printf("%d\n",arr[i]);
	}
	return 0;
}

근데 퀵소트로 푸니까 시간초과가 나왔다. 아마 퀵소트의 경우는 Best, Avg케이스는 NlogN이지만 Worst케이스는 N^2가 나올 수 있기 때문인 것 같다.

 

따라서, NlogN 정렬 중 합병정렬이나 힙 정렬로 접근해보고자 한다.

 

 

-> 합병정렬

#include <stdio.h>

int sorted[1000000];

void merge (int arr[], int start, int mid, int end)
{
	int i = start;
	int j = mid+1;
	int k = start;
	
	while(i<=mid && j<=end)
	{
		if (arr[i]<=arr[j])
		{
			sorted[k] = arr[i];
			i++;
		}
		else
		{
			sorted[k] = arr[j];
			j++;
		}
		k++;
	}
	
	if (i>mid)
	{
		int t;
		for (t=j; t<=end; t++)
		{
			sorted[k] = arr[t];
			k++;
		}
	}
	
	else
	{
		int t;
		for (t=start; t<=mid; t++)
		{
			sorted[k] = arr[t];
			k++;
		}
	}
	int t;
	for (t=start; t<=end; t++)
	{
		arr[t] = sorted[t];
	}
	
}

void merge_sort (int arr[], int start, int end)
{
	if (start<end)
	{
		int mid = (start+end)/2;
		merge_sort (arr,start,mid);
		merge_sort (arr,mid+1,end);
		merge (arr,start,mid,end);
	}
}

int main()
{
	int i,number = 0;
	scanf("%d",&number);
	int arr[number];
	
	
	for (i=0; i<number; i++)
	{
		scanf("%d",&arr[i]);
	}
	
	merge_sort (arr,0,number-1);
	for (i=0; i<number; i++)
	{
		printf("%d\n",arr[i]);
	}
	
	return 0;
}
반응형
반응형

 

풀이에 사용한 언어 : C언어

그리디 알고리즘
- 현재 주어진 Step만을 기준으로 하여, 최적의 해를 구하는 알고리즘이다.
다만, 매 Step마다 최적의 해를 구했다해도 최종 결과는 최적의 해가 아닐 수 있다. 그래서 불완전한 알고리즘이며 특수한 경우에만 그리디 알고리즘을 쓴다.

대표적인 예제가 거스름돈이다. 우리가 맥도날드에서 상하이스파이시버거세트를 산다하자. 한 때, 굉장히 좋아했는데 이사한 집 근처에 맥날이 없어서 가격이 기억안나지만 대충 5500원이였던 것 같다.
10000원을 지불하고 4500원을 거슬러 받는다 하면, 일반적으로 1000원 4장, 500원 1개로 총 5번의 거스름을 받는게 맞다. 화폐는 그 단위가 5000,1000,500,100... 이런 식으로 정해져 있고 내림차순 (혹은 오름차순)이기 때문에 그리디를 적용해도 괜찮다.

아래 코드 내용은, 돈을 입력받았을 때, (예를 들면 3100원이라 하자) 500원,100원,50원,10원만 갖고 있을 때 가장 최소한으로 거스름을 한 결과를 구현하고자 했다.

물론 아래 코드는 실행해보면 잘못되었음을 알 수 있다.

#include <stdio.h>

int main()
{
	int money;
	scanf("%d",&money);
	int n,m,k,l=0;
	
	while(money>0)
	{
		money = money%500;
		n = money/500;
		
		if (money<500)
		{
			money = money%100;
			m = money/100;
			if (money<100)
			{
				money = money%50;
				k = money/50;
				if (money<50)
				{
					money = money%10;
					l = money/10;
				}
			}
		}
		printf("%d",n+m+k+l);
		
	}
	
	return 0;
}

그럼 왜 틀렸는지 분석을 해봤는데, 단순히 코드의 순서가 잘못되었다고 판단 중이다.
보면 %연산자를 먼저한 값을 money에 집어넣고 그 다음 몫연산자를 카운팅했으니 값이 이상하게 나올 수 밖에 없다.
또한, if문은 필요가 없다. 반복을 수행하면서 알아서 500원 이하면 100으로 넘어갈테니까.

#include <stdio.h>

int main()
{
	int cnt = 0;
	int money;
	scanf("%d",&money);
	
	while (money>0)
	{
		cnt += money/500;
		money = money%500;
		
		cnt += money/100;
		money = money%100;
		
		cnt += money/50;
		money = money%50;
		
		cnt += money/10;
		money = money/10;
	}
	
	printf("%d",cnt);
	
	return 0;
}

그래서 재구성해본 결과는 이러하다.

잘 나온다. 4700원을 먼저 500원으로 거스르면 9번이고 나머지 200원은 100원으로 거스르면 2번이니까 총합 11번이다.
결과는 같다.

반응형

+ Recent posts