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

 

import sys

input = sys.stdin.readline

N = int(input().rstrip())
for _ in range(N):
    M = int(input().rstrip())
    clothes = {}

    for _ in range(M):
        (item, cloth_type) = list(input().rstrip().split())
        clothes[cloth_type] = clothes.get(cloth_type, 0) + 1

    result = 1
    for count in clothes.values():
        result *= (count + 1) #해당 종류의 의상을 선택하지 않는 경우 포함

    print(result - 1) # 아무 옷도 안 입는 경우 제외

 

의상 타입별 고르지 않는 경우도 카운트하는게 중요하다.

반응형

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

 

이 문제는 "다음 큰(또는 같은) 수(Next Greater or Equal Element)" 문제다. 배열이나 리스트에서 각 원소에 대해 오른쪽에 있는 원소들 중 자신보다 크거나 같은(또는 보통은 '큰' 경우가 많지만, 변형으로 '크거나 같은' 조건을 쓰기도 한다) 첫 번째 원소를 찾는 문제이다.

 

 

1. 단순 탐색 풀이 - O(n^2)

from functools import reduce
import sys

input = sys.stdin.readline
N = int(input().strip())
heights = list(int(input().strip()) for _ in range(N))

def get_answer():
    results = []

    while heights:
        height = heights.pop(0)
        count = 0
        for i in range(len(heights)):
            if heights[i] < height:
                count += 1
            else:
                break
        results.append(count)
    print(reduce(lambda acc, value: acc + value, results))

 

 

2. 스택 이용 최적화 풀이 - O(n)

from functools import reduce
import sys

input = sys.stdin.readline
N = int(input().strip())
heights = list(int(input().strip()) for _ in range(N))

def get_answer():
    stack = []
    total_visible = 0

    for i in range(N - 1, -1, -1):
        # 현재 빌딩보다 낮은 빌딩들은 스택에서 제거
        while stack and heights[stack[-1]] < heights[i]:
            stack.pop()

        # 스택에 차단 빌딩이 있으면 그 위치까지 빌딩 수 계산
        if stack:
            visible = stack[-1] - i - 1
        else:
            # 오른쪽의 모든 빌딩이 보이는 경우
            visible = N - i - 1
        total_visible += visible

        # 현재 빌딩 인덱스를 추가
        stack.append(i)

    return total_visible

print(get_answer())

 

  • 스택을 활용하면 오른쪽에서 왼쪽으로 한 번의 순회로 각 빌딩의 보이는 옥상 수를 구할 수 있어 전체 시간복잡도를 O(n)으로 최적화할 수 있다.
  • 이 방법은 각 빌딩이 스택에 단 한 번 들어가고 한 번씩만 제거되므로 효율적이다.

 

반응형

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

 

 

1. 단순 탐색 풀이 - O(n^2)

시간복잡도 O(n^2)를 가지는 모든 요소를 탐색하는 방식으로 풀이했다. 당연히, 시간초과로 실패했다.

N = int(input().strip())
heights = list(map(int, input().strip().split()))

def get_answer():
    answer = [0 for _ in range(N)]
    for i in range(len(heights), 0, -1):
        if i == 1:
            break
        current_top = heights[i - 1]
        for j in range(i - 1, 0, -1):
            left_top = heights[j - 1]
            if left_top >= current_top:
                answer[i - 1] = j
                break
    print(" ".join(map(str, answer)))

get_answer()

 

 

2. 스택 이용한 풀이 - 최악 O(n^2), 최선 O(n)

스택을 사용하면 각 요소를 한 번만 처리(삽입/삭제)하게 되어, 이미 검사한 요소들을 재검사하지 않아도 된다.

일반적으로 스택 기반 알고리즘에서는 리스트를 한 번 순회하며, 현재 요소보다 작은(또는 큰) 후보들은 스택에서 제거하는 식으로 진행하여, 각 요소가 스택에 한 번 push되고 최대 한 번 pop되도록 한다.

 

최악의 경우:
예를 들어, 만약 리스트가 내림차순 정렬되어 있다면, 각 요소마다 내부 for 루프가 거의 전체 남은 요소를 검사하게 된다.
따라서 전체 연산 횟수는 약 n+(n−1)+(n−2)+…+1=O(n^2)가 된다.

N = int(input().strip())
heights = list(map(int, input().strip().split()))

def get_answer():
    answer = [0 for _ in range(N)]

    while heights:
        height = heights.pop()

        for i in range(len(heights) - 1, -1, -1):
            if height < heights[i]:
                answer[len(heights)] = i + 1
                break

    print(" ".join(map(str, answer)))

get_answer()

 

 

3. 스택 이용한 풀이 - 최적화 O(n)

N = int(input().strip())
heights = list(map(int, input().strip().split()))

def get_answer():
    answer = [0 for _ in range(N)]
    stack = [] # 인덱스 저장

    for i in range(len(heights)):
        height = heights[i]
        # 스택에 있는 인덱스의 높이가 현재보다 작으면, 이들은 현재 탑의 신호를 받을 수 없음
        while stack and heights[stack[-1]] < height:
            stack.pop()

        # 스택이 남아 있으면, 스택의 최상단 인덱스가 현재 탑의 신호를 수신하는 탑이다
        if stack:
            answer[i] = stack[-1] + 1
        else:
            answer[i] = 0

        stack.append(i) # 현재 탑 인덱스 추가

    print(" ".join(map(str, answer)))

get_answer()
반응형

DFS(Depth-First Search)와 BFS(Breadth-First Search)는 그래프나 트리 탐색에서 사용하는 대표적인 탐색 알고리즘이다. 두 알고리즘 모두 특정 노드를 방문하고, 그 노드와 연결된 다른 노드들을 탐색하는 방식으로 동작한다. DFS는 깊이를 우선으로 탐색하고, BFS는 너비를 우선으로 탐색한다.

 

1. DFS (Depth-First Search, 깊이 우선 탐색)

DFS는 탐색할 때 가능한 한 깊게 들어가는 방식으로 탐색한다. 즉, 한 경로를 따라 끝까지 가고, 더 이상 갈 곳이 없으면 다시 돌아와 다른 경로를 탐색한다. 이 방식은 재귀스택을 사용해 구현할 수 있다.

 

DFS 예시: 그래프 탐색

function dfs(graph, start, visited = new Set()) {
    // 현재 노드 방문 처리
    visited.add(start);
    console.log(start);

    // 현재 노드와 연결된 노드들을 재귀적으로 방문
    for (let neighbor of graph[start]) {
        if (!visited.has(neighbor)) {
            dfs(graph, neighbor, visited);
        }
    }
}
// 그래프 표현 (인접 리스트)
const graph = {
    1: [2, 3],
    2: [4],
    3: [5],
    4: [],
    5: []
}

dfs(graph, 1);  // 출력: 1 2 4 3 5

 

DFS의 시간 복잡도

  • 시간 복잡도: O(V + E) (V는 노드 수, E는 간선 수)
  • 그래프의 모든 노드를 방문하고 모든 간선을 처리해야 하기 때문에, 위와 같은 복잡도를 가진다.

 

2. BFS (Breadth-First Search, 너비 우선 탐색)

BFS는 탐색할 때 가까운 노드부터 차례대로 방문한다. 즉, 특정 노드에서 연결된 모든 노드를 먼저 탐색한 후, 그 다음 깊이의 노드들을 탐색한다. 이 방식은 큐(Queue) 자료 구조를 사용해 구현한다.

 

BFS 예시: 최단 경로 탐색

function bfs(graph, start) {
    const queue = [start]; // 탐색할 노드를 저장할 큐
    const visited = new Set(); // 방문한 노드를 저장할 집합
    visited.add(start);

    while (queue.length > 0) {
        const node = queue.shift(); // 탐색할 노드를 큐에서 꺼냄
        console.log(node);

        for (let neighbor of graph[node]) {
            if (!visited.has(neighbor)) {
                visited.add(neighbor);
                queue.push(neighbor);
            }
        }
    }
}

// 그래프 표현 (인접 리스트)
const graph = {
    1: [2, 3],
    2: [4],
    3: [5],
    4: [],
    5: []
};

bfs(graph, 1);  // 출력: 1 2 3 4 5

 

BFS의 시간 복잡도

  • 시간 복잡도: O(V + E) (V는 노드 수, E는 간선 수)
  • 모든 노드와 모든 간선을 한 번씩 방문하기 때문에 DFS와 마찬가지로 같은 복잡도를 가진다.

 

DFS와 BFS의 차이

  • 탐색 방식: DFS는 깊이 우선으로 탐색하며, BFS는 너비 우선으로 탐색한다.
  • 구현 방식: DFS는 재귀나 스택을 사용하고, BFS는 큐를 사용해 구현한다.
  • 사용 사례:
    • DFS는 경로 탐색(예: 미로 찾기)나 백트래킹이 필요한 문제에 적합하다.
    • BFS는 그래프에서 최단 경로를 찾는 문제에 적합하다.

 

예시 문제

 

1. 미로 찾기

문제: N x M 크기의 미로가 주어질 때, 출발점에서 도착점까지 최단 경로를 찾으세요. 벽은 통과할 수 없으며, 상하좌우로만 이동할 수 있습니다.

 

// BFS
function shortestPath(maze, start, end) {
    const directions = [[0, 1], [1, 0], [0, -1], [1, 0]];
    const queue = [[start, 0]]; // [현재위치, 이동거리]
    const visited = new Set();
    visited.add(start.toString());

    while (queue.length > 0) {
        const [[x, y], distance] = queue.shift();

        if (x === end[0] && y === end[1]) return distance;

        for (let [dx, dy] of directions) {
            const newX = x + dx;
            const newY = y + dy;

            if (newX >= 0 && newY >= 0 &&
                newX < maze.length && newY < maze[0].length &&
                maze[newX][newY] === 0 && !visited.has([newX, newY].toString())
            ) {
                visited.add([newX, newY].toString());
                queue.push([[newX, newY], distance + 1]);
            }
        }
    }

    return -1; // 도착할 수 없는 경우
}

const maze = [
    [0, 1, 0, 0, 0],
    [0, 1, 0, 1, 0],
    [0, 0, 0, 1, 0],
    [0, 1, 1, 1, 0],
    [0, 0, 0, 0, 0]
];

console.log(shortestPath(maze, [0, 0], [4, 4]));  // 출력: 8 (최단 경로 거리)

 


2. 섬 개수 세기

문제: 2차원 배열에서 육지를 1, 바다를 0으로 나타내는 섬의 지도에서, 섬(연결된 1의 집합)의 개수를 구하세요.

 

// DFS
function numIslands(grid) {
    let count = 0;

    function dfs(grid, x, y) {
        if (x < 0 || y < 0 || x >= grid.length || y >= grid[0].length || grid[x][y] === '0') {
            return;
        }

        grid[x][y] = '0'; // 방문한 곳은 0 처리
        dfs(grid, x + 1, y);
        dfs(grid, x - 1, y);
        dfs(grid, x, y + 1);
        dfs(grid, x, y - 1);
    }

    for (let i = 0; i < grid.length; i++) {
        for (let j = 0; j < grid[0].length; j++) {
            if (grid[i][j] === '1') {
                count++;
                dfs(grid, i, j);
            }
        }
    }

    return count;
}

const grid = [
    ['1', '1', '0', '0', '0'],
    ['1', '1', '0', '0', '0'],
    ['0', '0', '1', '0', '0'],
    ['0', '0', '0', '1', '1']
];

console.log(numIslands(grid));  // 출력: 3 (3개의 섬)

 

반응형

 

 

10866번: 덱

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지

www.acmicpc.net

 

 

<내 코드>

import sys

class Deque:
    def __init__(self):
        self.result = list()

    def push_front(self, num):
        self.result.insert(0, num)

    def push_back(self, num):
        self.result.append(num)

    def pop_front(self):
        if(self.empty()):
            return -1
        else:
            return self.result.pop(0)

    def pop_back(self):
        if(self.empty()):
            return -1
        else:
            return self.result.pop()

    def front(self):
        if(self.empty()):
            return -1
        else:
            return self.result[0]

    def back(self):
        if(self.empty()):
            return -1
        else:
            return self.result[-1]

    def size(self):
        return len(self.result)

    def empty(self):
        if(self.size() == 0):
            return 1
        else:
            return 0


N = int(sys.stdin.readline())
deque = Deque()

for _ in range(N):
    input_split = sys.stdin.readline().split()
    oper = input_split[0]

    if(oper == 'push_front'):
        deque.push_front(int(input_split[1]))
    elif(oper == 'push_back'):
        deque.push_back(int(input_split[1]))
    elif(oper == 'pop_front'):
        print(deque.pop_front())
    elif(oper == 'pop_back'):
        print(deque.pop_back())
    elif(oper == 'front'):
        print(deque.front())
    elif(oper == 'back'):
        print(deque.back())
    elif(oper == 'empty'):
        print(deque.empty())
    elif(oper == 'size'):
        print(deque.size())

 

반응형

 알고리즘, 자료구조 - 순차 탐색(선형 탐색) 

 

순차 탐색이란?

 

리스트 또는 배열 안에 있는 첫 번째 자료부터 하나씩 비교하면서 원하는 데이터를 찾아가는 알고리즘이다. 데이터를 따로 조작할 필요가 없어 단순하지만 비효율적이라는 단점을 지니고 있다. 추가로 순차 탐색은 단방향으로 탐색을 수행하기 때문에 선형 탐색(Linear Search)라고도 부른다.

순차 탐색은 최악의 경우에 최대 n번의 비교를 필요로 하기에 시간 복잡도는 O(n)이 된다.

이는 데이터가 n개가 되면 그에 따른 연산 수도 n개를 따라가기 때문에 자료수가 많아질수록 연산의 속도가 느리다는 단점이 있다.

 

 

 

예제

 

# 리스트에서 특정 숫자의 위치 찾기

def search_num(a, x):
	n = len(a)
    for i in range(0, n): #리스트 a의 모든 값에 대해 반복
    	if x == a[i] : 	# 찾는 숫자와 값이 일치하면
        	return i	# 인덱스 값 반환
    
    return -1
    
a = [17, 92, 18, 33, 58]
print(search_num(a, 18)) # 2 출력
print(search_num(a, 900)) # -1 출력(해당 값이 없기에)

 

# 학생 번호를 입력시 해당하는 학생의 이름을 반환
def find_student(number, stuNo, stuName):
    num = len(stuNo)
    for i in range(0, num):
        if(stuNo[i] == number):
            return stuName[i]

    return "해당 학생은 없습니다."


stu_no = [39, 14, 67, 103]
stu_name = ["Justin", "John", "Mike", "Summer"]

print(find_student(39, stu_no, stu_name)) #출력 - "Justin"
 

 

반응형

+ Recent posts