LeetCode 1260. Shift 2D Grid

문제 https://leetcode.com/problems/shift-2d-grid 풀이 $m \times n$ 2d 그리드에 있는 원소들을 오른쪽으로 k번 민 결과를 리턴하면 된다. grid[row][n - 1] → grid[row + 1][0] : 마지막 열의 원소는 다음 행의 첫 번째 열로 이동 grid[m - 1][n - 1] → grid[0][0] : 마지막 행의 마지막 열 원소는 첫 번째 행의 첫 번째 열로 이동 k번 직접 반복하는 대신, 그리드를 1차원으로 평탄화하면 shift 연산이 단순한 배열 회전으로 바뀐다. 평탄화한 배열을 오른쪽으로 k칸 회전시킨 뒤, 다시 $m \times n$ 2차원 배열로 복원하면 된다. 단, k가 배열 길이 $m \times n$ 보다 클 수 있으므로 모듈러 연산으로 정규화한다. ...

July 20, 2026

LeetCode 3754. Concatenate Non-Zero Digits and Multiply by Sum I

문제 https://leetcode.com/problems/concatenate-non-zero-digits-and-multiply-by-sum-i 풀이 문제에서 주어진 조건에 따라서 스트링을 조작하면 되는 간단한 문제이다. 인티저에서 스트링으로, 스트링에서 인티저로 타입 컨버전만 조심하면된다. 주어진 정수 n을 스트링으로 변환한 다음, 앞에서부터 순회하여 "0"을 제거한 스트링 filtered를 만든다. filered를 인티저로 타입 컨버전 해서 x를 구한다. filered의 각 원소를 인티저로 컨버전 한 다음, 합을 구해서 sum을 구한다. x * sum을 리턴한다. 코드 class Solution: def sumAndMultiply(self, n: int) -> int: filtered = "".join(char for char in str(n) if char != "0") if not filtered: return 0 return int(filtered) * sum(map(int, filtered))

July 7, 2026

Archive. NVIDIA Physical AI 생태계 정리 노트

최근 NVIDIA의 피지컬 AI 생태계를 접할 기회가 있었는데, Omniverse, Isaac Sim, Cosmos, GR00T처럼 처음 보는 용어가 한꺼번에 등장해서 각각이 어떤 역할을 하는지 궁금해졌다. 조금 찾아보니 개별 기술을 각각 봐서 이해하긴 어렵고, 전체 구조를 보면서 어떤 역할을 하는지 이해하는 것이 중요하다는 생각이 들었다. 그래서 나중에 다시 봐도 흐름을 빠르게 떠올릴 수 있도록 핵심 개념을 간단히 정리했다. 피지컬 AI 현실 세계를 인식 → 판단 → 물리적으로 행동하는 AI 시스템 전체를 의미한다. 디지털 AI와 달리 센서 불확실성, 실시간 제어, 물리 상호작용이 핵심 난이도다. (현직자도 단순히 소프트웨어로 처리하는 것 만으로는 충분하지 않다고 했다.) ...

February 6, 2026

Programmers. 셔틀버스

문제 https://school.programmers.co.kr/learn/courses/30/lessons/17678 풀이 단순 구현으로 풀 수 있는 문제다. 문제를 단순화하면 조건에 맞는 최대 값을 구하는 문제이므로 바이너리 서치도 쓸 수 있겠다. 우선 시간을 다루는 문제에서는 시간, 분을 분으로 통일하는 경우가 더 시간을 다루기 쉬워지는 경우가 많다. 예를 들어서 셔틀의 첫 도착 시간인 9:00을 540으로 바꾸는 것이다. 그리고 timetable 배열의 길이가 최대 2000이므로 모든 배열을 순회해도, 심지어 $O(n^2)$시간으로 순회해도 아무런 걱정이 없다. 따라서 앞 번호부터 순서대로 버스에 태우면 된다. 이제 마지막 버스인 경우에 콘이 탈 수 있는 여유가 있다면, 마지막 버스의 도착 시간을 리턴하면 되고, 만약 여유가 없다면, 마지막 버스에 마지막으로 타는 사람보다 ‘1분’ 일찍 나오면 된다. ...

August 3, 2025

BOJ 14891. 톱니바퀴

문제 https://www.acmicpc.net/problem/14891 풀이 상황 설명이 굉장히 난해하고 비직관적이므로 글을 잘 읽어야 한다. 문제를 겨우 이해하고 나면 알고리즘이나 구현 능력 자체는 크게 요구하지 않는다. 글을 이해하는것 자체가 이 문제의 가장 큰 난관이다. 이 문제에서 톱니바퀴는 현실의 톱니바퀴처럼 움직이지 않는다. 이전 톱니바퀴가 회전하면 다음 톱니바퀴도 회전해야할지 말아야 할지를 이미 돌아간 상태의 이전 톱니바퀴가 아닌 돌기 전의 이전 톱니바퀴의 상태로 판단해야 한다. 코드 class Gear(): def __init__(self, stat): self.__stat = stat self.__top = 0 self.__left = 6 self.__right = 2 def rotate(self, drct): self.__top = (self.__top - drct) % 8 self.__left = (self.__left - drct) % 8 self.__right = (self.__right - drct) % 8 @property def top(self): return self.__stat[self.__top] @property def left(self): return self.__stat[self.__left] @property def right(self): return self.__stat[self.__right] def left_shift(gear_num, drct): new_drct = drct * -1 cur_gear = gear_num for next_gear in range(gear_num - 1, 0, -1): if gears[next_gear].right != gears[cur_gear].left: rotate_queue.append((next_gear, new_drct)) cur_gear = next_gear new_drct *= -1 else: break def right_shift(gear_num, drct): new_drct = drct * -1 cur_gear = gear_num for next_gear in range(gear_num + 1, 5): if gears[next_gear].left != gears[cur_gear].right: rotate_queue.append((next_gear, new_drct)) cur_gear = next_gear new_drct *= -1 else: break gear1 = Gear(list(map(int, input()))) gear2 = Gear(list(map(int, input()))) gear3 = Gear(list(map(int, input()))) gear4 = Gear(list(map(int, input()))) gears = [None, gear1, gear2, gear3, gear4] k = int(input()) answer = 0 rotate_queue = [] for _ in range(k): num, drct = map(int, input().split()) rotate_queue.append((num, drct)) left_shift(num, drct) right_shift(num, drct) for g, d in rotate_queue: gears[g].rotate(d) rotate_queue = [] rank = 1 for idx in range(1, 5): answer += gears[idx].top * rank rank *= 2 print(answer)

April 19, 2024

BOJ 14890. 경사로

문제 https://www.acmicpc.net/problem/14890 풀이 경사로의 방향을 생각해보면 왼쪽에서 오른쪽으로 올라가는 방향이 있을 것이고, 그 반대인 오른쪽에서 왼쪽으로 올라가는 방향도 있을 것이다. 이러한 경우에는 한 번에 해결하기 보다는 배열을 정방향, 역방향으로 각각 순회하여 경사로를 만들어주는 것이 좋다. 따라서 알고리즘은 다음과 같다. 주어진 2차원 배열을 Transpose한 배열을 하나 더 만든다. 세로 모양의 길을 찾기 위해서이다. 정방향으로 우선 순회한다. 다음에 있는 칸의 높이가 현재 칸보다 같으면 count를 하나 늘리고(count는 경사로를 지을 수 있는지 판단할 때 사용한다.) 1 낮으면 일단은 패스(역방향에서 확인한다), 1 높으면 경사로를 설치할 수 있는지 판단한다. 경사로를 설치할 수 있는지 판단하는 기준은 두 가지이다. 일단 경사로를 설치하기 충분한 공간이 확보되었는가 (count로 충분한 공간이 얼마나 있는지를 알 수 있다.), 만약 가능하다면 이미 그곳에 경사로가 설치되어 있지는 않는가(이는 is_built 배열로 추적한다. 하지만 정방향을 먼저 하므로 지금 상황에선 필요 없다.) 역방향으로 순회한다. 정방향과 같지만 정방향을 순회하면서 경사로를 지은 곳을 유의할 필요가 있다. 코드 def check(line): is_built = [False] * n current_height = line[0] count = 1 for idx in range(1, n): if current_height == line[idx]: count += 1 elif current_height - line[idx] == 1: current_height = line[idx] continue elif current_height - line[idx] == -1: if count >= l: for back in range(1, l + 1): is_built[idx - back] = True count = 1 else: return False current_height = line[idx] else: return False current_height = line[n - 1] count = 1 for idx in range(n - 2, -1, -1): if current_height == line[idx]: count += 1 elif current_height - line[idx] == 1: current_height = line[idx] continue elif current_height - line[idx] == -1: if count >= l: for back in range(1, l + 1): if is_built[idx + back]: return False is_built[idx + back] = True count = 1 else: return False current_height = line[idx] else: return False return True n, l = map(int, input().split()) graph = [] graph_tranposed = [[0] * n for _ in range(n)] answer = 0 for _ in range(n): graph.append(list(map(int, input().split()))) for row in range(n): for column in range(n): graph_tranposed[row][column] = graph[column][row] for idx in range(n): answer += 1 if check(graph[idx]) else 0 answer += 1 if check(graph_tranposed[idx]) else 0 print(answer)

April 19, 2024

BOJ 14499. 주사위 굴리기

문제 https://www.acmicpc.net/problem/14499 풀이 특별한 알고리즘이나 문제 해결 기법을 사용할 필요 없이, 순수하게 딕셔너리 자료구조만을 사용해서 풀 수 있는 문제이다. 주어진 주사위의 전개도를 이용하여 각 면에 숫자를 붙이고(면에 써져있는 숫자가 아닌 각 면을 인식하게 해주는 숫자, 이하 ID숫자라고 하겠다.) 이를 방향: ID 숫자의 꼴로 딕셔너리를 생성한다. 또 ID숫자: 면에 적힌 숫자 꼴로 딕셔너리를 하나 더 생성하여 두개의 딕셔너리로 주사위의 방향과 각 면에 적혀있는 숫자를 추적할 수 있다. 코드 import copy def north(): global dice_drct original_drct = copy.deepcopy(dice_drct) for new, ori in zip(["u", "l", "n", "e", "w", "s"], ["s", "n", "u", "e", "w", "l"]): dice_drct[new] = original_drct[ori] def south(): global dice_drct original_drct = copy.deepcopy(dice_drct) for new, ori in zip(["u", "l", "n", "e", "w", "s"], ["n", "s", "l", "e", "w", "u"]): dice_drct[new] = original_drct[ori] def east(): global dice_drct original_drct = copy.deepcopy(dice_drct) for new, ori in zip(["u", "l", "n", "e", "w", "s"], ["w", "e", "n", "u", "l", "s"]): dice_drct[new] = original_drct[ori] def west(): global dice_drct original_drct = copy.deepcopy(dice_drct) for new, ori in zip(["u", "l", "n", "e", "w", "s"], ["e", "w", "n", "l", "u", "s"]): dice_drct[new] = original_drct[ori] def change(): if graph[x][y] == 0: graph[x][y] = dice_num[dice_drct["l"]] else: dice_num[dice_drct["l"]] = graph[x][y] graph[x][y] = 0 def check(x, y): if x < 0 or x >= n or y < 0 or y >=m: return False return True n, m, x, y, k = map(int, input().split()) graph = [] for _ in range(n): graph.append(list(map(int, input().split()))) orders = list(input().split()) dice_num = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0} dice_drct = {"u": 1, "l": 6, "n": 2, "e": 3, "w": 4, "s": 5} for order in orders: isTrue = False if order == "1": if check(x, y + 1): y += 1 east() change() isTrue = True if order == "2": if check(x, y - 1): y -= 1 west() change() isTrue = True if order == "3": if check(x - 1, y): x -= 1 north() change() isTrue = True if order == "4": if check(x + 1, y): x += 1 south() change() isTrue = True if isTrue: print(dice_num[dice_drct["u"]])

April 15, 2024

BOJ 2331. 반복 수열

문제 https://www.acmicpc.net/problem/2331 풀이 수열을 만드는 반복문을 만든다. 도중에 기존에 있는 원소와 같은 원소가 나오면 반복문을 종료한다. 처음으로 나온 같은 원소의 인덱스를 출력한다. (인덱스는 0부터 시작이기에 더하고 뺄 필요가 없다.) 너무 간단하게 풀렸는데 시간복잡도가 별로 좋아보이진 않는다. 더 좋은 방법이 있을듯 싶다. 코드 a, p = map(int, input().split()) seq = [a] while True: element = 0 for i in str(seq[-1]): element += (int(i) ** p) if element in seq: break seq.append(element) print(seq.index(element))

November 7, 2022