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

 

11779번: 최소비용 구하기 2

첫째 줄에 도시의 개수 n(1≤n≤1,000)이 주어지고 둘째 줄에는 버스의 개수 m(1≤m≤100,000)이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스

www.acmicpc.net


풀이 과정

어떤 도시에서 출발해, 도착지점까지 버스를 타고 가는 최소 비용을 구하고, 그 경로를 출력하는 문제이다.

 

음수 가중치가 존재하지 않는, 가중치가 존재하는 그래프에서의 최소 비용을 구하는 문제이므로 다익스트라 알고리즘을 활용해서 문제를 해결할 수 있다. 우선순위 큐를 활용한 다익스트라 알고리즘의 시간복잡도는 O(ElogV)이므로 TLE도 발생하지 않음을 확인할 수 있다.

 

다익스트라 알고리즘 진행 도중, 다음 정점까지 가는 비용이 현재 정점을 경유하는 경우가 더 저렴하면, pre[nxt] = cur로 nxt 정점을 최소 비용으로 방문하기 위해서는 이전 정점으로 cur 정점을 거쳐야 한다는 점을 표시해주자. 그렇다면 end까지 도착했을때, end부터 계속 pre 배열을 확인하면 start부터 end까지 최소비용으로 방문했을 때의 경로를 역순으로 얻어낼 수 있으며, 이를 다시 뒤집어서 출력하면 경로를 반환할 수 있다.


소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {
	static StringBuilder sb = new StringBuilder();

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine()); // 도시의 개수를 입력받는다
		int m = Integer.parseInt(br.readLine()); // 버스의 개수를 입력받는다
		ArrayList<ArrayList<int[]>> al = new ArrayList<>(); // u번째 버스에서 출발하는 (비용, 도착지) 버스 노선을 저장한다
		int[] d = new int[n+1]; // 시작지로부터의 비용을 저장한다
		int[] pre = new int[n+1]; // 시작지점부터 특정 도시까지 최소비용으로 도달하기 위한 경로의 바로 이전 도시를 저장한다
		
		for (int i = 0; i <= n; i++) {
			al.add(new ArrayList<>()); // 인접 리스트 초기화
			d[i] = Integer.MAX_VALUE; // 시작지로부터의 비용을 MAX로 초기화한다
		}
		
		for (int i = 0; i < m; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			int u = Integer.parseInt(st.nextToken()); // 출발지 입력
			int v = Integer.parseInt(st.nextToken()); // 도착지 입력
			int w = Integer.parseInt(st.nextToken()); // 비용 입력
			al.get(u).add(new int[] {w, v}); // u번째 버스에서 출발하는 버스들의 리스트에 (비용, 도착지) 삽입
		}
		
		StringTokenizer st = new StringTokenizer(br.readLine());
		int start = Integer.parseInt(st.nextToken()); // 시작지점 입력
		int end = Integer.parseInt(st.nextToken()); // 도착지점 입력
		
		// (비용, 도착지)에서 비용의 오름차순으로 우선순위를 갖는 우선순위 큐 선언
		PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[0] - o2[0]);
		d[start] = 0; // 시작지점까지의 거리는 0
		pq.add(new int[] {d[start], start}); // pq에 가장 거리가 가까운 점을 넣는다
		
		// 다익스트라 진행
		while(!pq.isEmpty()) {
			int[] cur = pq.poll(); // 우선순위 큐에서 하나 빼고
			
			// 뺀 정점까지의 거리가 갱신된 시작정점까지의 거리와 다르면 쓰레기 값이므로 다른 값을 뺀다
			if (cur[0] != d[cur[1]]) continue; 
			
			// 현재 보고 있는 정점에서 경로가 존재하는 모든 도착도시에 대해
			for (int[] nxt : al.get(cur[1])) {
				
				// 도착도시까지의 거리가 현재 보고있는 정점을 거쳐가는게 더 비용이 적은 경우에 갱신한다
				if (d[nxt[1]] <= d[cur[1]] + nxt[0]) continue;
				d[nxt[1]] = d[cur[1]] + nxt[0];
				
				// 그리고 우선순위 큐에 넣는다
				pq.add(new int[] {d[nxt[1]], nxt[1]});
				// 현재 정점을 거쳐가는 것이 nxt까지의 경로중 가장 비용이 적으므로, 저장한다
				pre[nxt[1]] = cur[1];
			}
		}
		
		// 최소 비용을 출력한다
		sb.append(d[end]).append('\n');
		
		// 도착정점부터 pre 배열을 살펴보며 최소 비용으로 순회하는 경로를 역순으로 스택에 삽입한다
		ArrayDeque<Integer> q = new ArrayDeque<>();
		int index = end;
		q.addLast(index);
		
		while (index != start) {
			index = pre[index];
			q.addLast(index);
		}
		
		sb.append(q.size()).append('\n');
		
		// 스택에서 다시 빼면서 출력하면 출발도시에서 도착도시까지의 경로가 출력된다
		while(!q.isEmpty()) {
			sb.append(q.pollLast()).append(' ');
		}
		
		System.out.print(sb.toString());
	}
}

 

 

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

 

1197번: 최소 스패닝 트리

첫째 줄에 정점의 개수 V(1 ≤ V ≤ 10,000)와 간선의 개수 E(1 ≤ E ≤ 100,000)가 주어진다. 다음 E개의 줄에는 각 간선에 대한 정보를 나타내는 세 정수 A, B, C가 주어진다. 이는 A번 정점과 B번 정점이

www.acmicpc.net


풀이 과정

그래프에서 최소 스패닝 트리를 구하는 문제이다.

 

최소 스패닝 트리를 구하는 방법은 크루스칼 알고리즘, 프림 알고리즘이 존재하고 일반적으로 구현이 간단한 크루스칼 알고리즘을 많이 사용한다.

 

크루스칼 알고리즘에서는, 가중치가 가장 낮은 간선을 우선으로 노드를 연결하는데, 이미 연결되어 있는 노드상의 간선이면 연결하지 않는다. 이 때, 이미 연결되어 있는지 여부의 판단을 서로소 집합으로 구하게 된다.


소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {
	static StringBuilder sb = new StringBuilder();
	static int[] parents;
	
	static void union(int a, int b) { // 서로소 집합 합치기
		int aRoot = find(a);
		int bRoot = find(b);
		if (aRoot == bRoot) return;
		parents[bRoot] = aRoot;
	}
	
	static int find(int a) { // 서로소 집합의 대표자 찾기
		if (a == parents[a]) return a;
		else return parents[a] = find(parents[a]); // 경로 압축
	}

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());
		int V = Integer.parseInt(st.nextToken());
		int E = Integer.parseInt(st.nextToken());
		parents = new int[V+1];
		
		// 가중치가 가장 작은 것을 가지고 있는 간선을 루트로 하는 최소 힙 선언
		PriorityQueue<int[]> edges = new PriorityQueue<>((o1, o2) -> o1[0] - o2[0]);
		int answer = 0;
		int connect = 0;
		
		for (int i = 0; i <= V; i++) {
			parents[i] = i; // 대표자 초기화
		}
		
		for (int i = 0; i < E; i++) {
			st = new StringTokenizer(br.readLine());
			int u = Integer.parseInt(st.nextToken());
			int v = Integer.parseInt(st.nextToken());
			int w = Integer.parseInt(st.nextToken());
			
			edges.add(new int[] {w, u, v}); // (가중치, 시작노드, 도착노드 순으로 입력)
		}
		
		while (connect < V-1) { // 간선-1개의 간선이 연결되면 최소 스패닝 트리 완성
			int[] cur = edges.poll();
			
			// 같은 서로소 집합에 속한지 파악해서 이미 트리에 연결되어 있는 노드끼리의 간선이면 무시한다
			if (find(cur[1]) == find(cur[2])) {
				continue;
			}
			
			// 정답 가중치 증가, 이어진 간선 개수 증가, 같은 서로소 지합으로 만든다
			answer += cur[0];
			connect++;
			union(cur[1], cur[2]);
		}
		
		System.out.println(answer);
	}
}

 

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

 

7662번: 이중 우선순위 큐

입력 데이터는 표준입력을 사용한다. 입력은 T개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 입력 데이터의 수를 나타내는 정수 T가 주어진다. 각 테스트 데이터의 첫째 줄에는 Q에 적

www.acmicpc.net


풀이 과정

최소값과 최대값을 뽑아낼 수 있는 이중 우선순위 큐를 구현하는 문제이다.

 

최소 힙, 최대 힙 2개를 선언해서 풀이하는 방법도 있지만 자바에서는 최소값 반환과 최대값 반환이 O(logN)으로 가능한 이진 검색 트리, 그 중에서도 레드 블랙 트리로 구현된 트리셋 자료구조가 존재해 트리셋 자료구조를 활용해 문제를 해결하였다.

 

트리셋은 Set이므로, 중복된 수를 여러개 담을 수 없기에, 특정 수의 개수를 저장할 해쉬맵을 선언하여 문제를 해결하였다. 소스의 주석을 보면 이해할 수 있다.


소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.TreeSet;

public class Main {
	static StringBuilder sb = new StringBuilder();

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int T = Integer.parseInt(br.readLine());
		
		for (int tc = 1; tc <= T; tc++) {
			int k = Integer.parseInt(br.readLine());
			HashMap<Integer, Integer> hm = new HashMap<>(); // 숫자의 개수를 저장할 해쉬맵
			TreeSet<Integer> ts = new TreeSet<>(); // 이중 우선순위 큐 역할을 할 트리셋
			for (int i = 0; i < k; i++) {
				StringTokenizer st = new StringTokenizer(br.readLine());
				String command = st.nextToken();
				int input = Integer.parseInt(st.nextToken());
				
				if (command.equals("I")) { // 입력이면
					// 이미 입력받았던 거면 개수를 증가시킨다
					if (hm.containsKey(input)) hm.put(input, hm.get(input)+1);
					
					// 없었던거면 해쉬맵에 개수 1개로 표시하고 트리셋에 넣는다
					else {
						hm.put(input, 1);
						ts.add(input);
					}
				} else {
					if (ts.isEmpty()) continue; // 비었는데 삭제 연산은 무시
					
					if (input == 1) { // 최대값 출력
						int maxValue = ts.last();
						
						// 같은 수를 2개 이상 담겨있으면 해쉬맵을 보고 개수만 하나 줄여준다
						if (hm.get(maxValue) > 1) hm.put(maxValue, hm.get(maxValue) - 1);
						
						// 아니면 해쉬맵에서 제거하고 트리셋에서도 제거한다
						else {
							ts.pollLast();
							hm.remove(maxValue);
						}
					} else { // 최소값 출력에서도 같은 연산을 수행한다
						int minValue = ts.first();
						if (hm.get(minValue) > 1) hm.put(minValue, hm.get(minValue) - 1);
						else {
							ts.pollFirst();
							hm.remove(minValue);
						}
					}
				}
			}
			
			if (ts.isEmpty()) {
				sb.append("EMPTY");
			} else {
				sb.append(ts.last()).append(' ').append(ts.first());
			}
			
			sb.append('\n');
		}
		
		System.out.print(sb.toString());
	}
}

 

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

 

11779번: 최소비용 구하기 2

첫째 줄에 도시의 개수 n(1≤n≤1,000)이 주어지고 둘째 줄에는 버스의 개수 m(1≤m≤100,000)이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스

www.acmicpc.net


풀이 과정

도시와 도시 사이를 운행하는 버스가 있을 때, A번째 도시에서 B번째 도시로 가는 최소비용과 경로를 출력하는 문제이다.

  • 음수 간선 없는 최단거리 -> 다익스트라 알고리즘
  • 특정 정점 i를 최단거리로 방문하기 위해서 바로 직전 정점으로 j를 방문해야 한다면 pre[i] = j로 표시
  • 마지막 정점에서 역추적하면서 최단경로를 역순으로 뽑아낼 수 있으며, 이를 다시 역순으로 출력해주면 된다.

소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.StringTokenizer;

public class Main {
	static StringBuilder sb = new StringBuilder();

	public static void main(String[] args) throws IOException { // 메모리 56776KB 시간 632ms
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int n = Integer.parseInt(br.readLine());
		int m = Integer.parseInt(br.readLine());
		ArrayList<ArrayList<int[]>> al = new ArrayList<>();
		int[] d = new int[n+1];
		int[] pre = new int[n+1];
		
		for (int i = 0; i <= n; i++) {
			al.add(new ArrayList<>());
			d[i] = Integer.MAX_VALUE;
		}
		
		for (int i = 0; i < m; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			int u = Integer.parseInt(st.nextToken());
			int v = Integer.parseInt(st.nextToken());
			int w = Integer.parseInt(st.nextToken());
			al.get(u).add(new int[] {w, v});
		}
		
		StringTokenizer st = new StringTokenizer(br.readLine());
		int start = Integer.parseInt(st.nextToken());
		int end = Integer.parseInt(st.nextToken());
		PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[0] - o2[0]);
		d[start] = 0;
		pq.add(new int[] {d[start], start});
		
		while(!pq.isEmpty()) {
			int[] cur = pq.poll();
			
			if (cur[0] != d[cur[1]]) continue;
			
			for (int[] nxt : al.get(cur[1])) {
				if (d[nxt[1]] <= d[cur[1]] + nxt[0]) continue;
				d[nxt[1]] = d[cur[1]] + nxt[0];
				pq.add(new int[] {d[nxt[1]], nxt[1]});
				
				// nxt를 갈 때 cur를 거쳐가는게 더 빠르다면, pre[nxt] = cur로 설정
				pre[nxt[1]] = cur[1]; 
			}
		}
		
		sb.append(d[end]).append('\n');
		
		
		// 스택을 이용하여 끝에서부터 역추적
		ArrayDeque<Integer> q = new ArrayDeque<>();
		int index = end;
		q.addLast(index);
		
		while (index != start) {
			index = pre[index];
			q.addLast(index);
		}
		
		sb.append(q.size()).append('\n');
		
		// 스택에 넣은 뒤 다시 다 빼면 뒤집어서 나온다
		while(!q.isEmpty()) {
			sb.append(q.pollLast()).append(' ');
		}
		
		System.out.print(sb.toString());
	}
}

 

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV5V4A46AdIDFAWu&& 

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com


풀이 과정

문제의 조건에 맞게 주어진 벌통을 선택할 때, 선택한 벌통내에서 채취할 수 있는 가치의 최대값을 구하는 문제이다.

 

일꾼이 벌통을 선택하는 경우를 중복 순열의 경우의 수를 만들어 구하였고, 각 벌통의 칸에서 꿀을 퍼갈지 안퍼갈지를 부분집합의 경우의 수를 만들어 구하였다.

 

N, M이 크지 않은 수로 주어지므로 완전 탐색으로 풀 수 있음을 추측할 수 있다. 로직을 세우고 시간복잡도를 계산해도 문제에서 주어진 시간을 초과하지 않음을 알 수 있다. 설명은 코드에 주석으로 달아놓았다.


소스 코드

def perm(index):  # 첫번째 벌꿀통의 r, c 두번째 벌꿀통의 r, c를 중복순열로 생성
    if index == 4:
        check()  # 값을 구하러 간다
        return

    for i in range(N):
        numbers.append(i)  # 이번 수를 중복 순열에 넣고
        perm(index+1)  # 다음 index의 수를 고르러 간다
        numbers.pop()  # 이번 수를 중복 순열에 넣지 않고 이번 index에 넣을 다음 수를 찾으러 간다


def check():
    global answer, max1
    a, b, c, d = numbers[0], numbers[1], numbers[2], numbers[3]
    if b > N-M or d > N-M:  # M칸 크기의 벌꿀 통을 가로줄에 넣을 수 없는 경우 진행하지 않는다
        return
    selected = [[False for _ in range(N)] for _ in range(N)]  # 벌꿀통이 놓인 곳을 표시
    subset_selected = [[False for _ in range(N)] for _ in range(N)]  # 벌꿀통 안에서 벌꿀을 퍼갈 공간 표시
    for i in range(M):
        selected[a][b+i] = True  # 벌꿀통을 놓았다고 표시한다
    max1 = 0
    subset(a, b, 0, subset_selected)  # 벌꿀통 안에서 퍼간 벌꿀의 양이 C를 넘지 않으면서 최대 가치를 지니는 경우가 언제인지
    # 부분집합으로 모든 경우를 만들어서 판단한다
    max2 = max1

    for i in range(M):
        if selected[c][d+i]:  # 2번째 벌꿀 통을 놓는데 1번째 벌꿀 통이랑 겹치면 진행하지 않는다
            return
        selected[c][d+i] = True
    max1 = 0
    subset(c, d, 0, subset_selected)  # 2번째 벌꿀 통 안에서 1번째 벌꿀통 부분집합 만들기에서 했던 것과 동일 행위 진행

    answer = max(answer, max1 + max2)  # 정답 갱신


def subset(i, j, index, subset_selected):  # 부분집합 경우의 수 생성 코드
    global max1
    if index == M:  # 모든 벌꿀통의 칸에 대해 진행했으면
        temp = 0
        values = 0
        for k in range(M):
            if subset_selected[i][j+k]:  # 선택된 칸의
                temp += board[i][j+k]  # 양의 누적을 저장하고
                values += board[i][j+k]**2  # 가치의 누적을 저장한다
        if temp > C:  # 양이 C를 넘었으면 담을 수 없으므로 진행하지 않는다
            return
        max1 = max(max1, values)  # 양이 C를 넘지 않았다면 가치 누적값의 최대를 갱신한다
        return

    subset_selected[i][j + index] = True  # 벌꿀통의 특정 칸을 고르는 경우
    subset(i, j, index+1, subset_selected)  # 재귀 진행
    subset_selected[i][j+index] = False  # 벌꿀통의 특정 칸을 고르지 않는 경우
    subset(i, j, index + 1, subset_selected)  # 재귀 진행


T = int(input())

for tc in range(1, T+1):
    max1 = 0
    N, M, C = map(int, input().split())
    board = [list(map(int, input().split())) for _ in range(N)]  # 2차원 배열 1줄로 받는 코드
    answer = 0  # 정답을 저장할 변수
    numbers = []  # 중복순열을 생성할 코드
    perm(0)  # 중복순열 재귀
    print(f"#{tc} {answer}")  # 정답 출력

 

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

 

1406번: 에디터

첫째 줄에는 초기에 편집기에 입력되어 있는 문자열이 주어진다. 이 문자열은 길이가 N이고, 영어 소문자로만 이루어져 있으며, 길이는 100,000을 넘지 않는다. 둘째 줄에는 입력할 명령어의 개수

www.acmicpc.net


풀이 과정

문자열이 주어지고, 문자열 안에서 커서를 왼쪽, 오른쪽으로 이동시키며 커서 왼쪽의 문자를 추가하거나 삭제시키는 연산을 구현하는 문제이다.

 

문자열을 배열로 생각한다면 문자열에서 문자를 추가하거나 삭제하는 연산은 O(N)의 시간복잡도를 가진다. 추가된 문자 뒤쪽에 있는 문자를 한 칸씩 밀거나, 삭제된 문자 뒤쪽에 있는 문자들을 한 칸씩 당겨야 하기 때문이다. 따라서 문자의 추가와 삭제가 O(1)에 이루어질 수 있는 링크드 리스트를 사용해 문제를 해결해야 한다.

 

이 문제는 추가, 삭제 연산이 입력으로 들어오는 index에서 이루어 지는 것이 아닌, 커서 왼쪽에서 이루어진다는 것이 고정되어 있으므로 2개의 스택을 활용하여, 왼쪽 스택에는 커서 왼쪽에 있는 문자들을 저장, 오른쪽 스택에는 커서 오른쪽에 있는 문자들을 저장하는 방법으로 문제를 해결할 수도 있다.


소스 코드

2개의 스택 - 자바

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.StringTokenizer;

public class Main { // 메모리 : 142208KB / 시간 608ms
	static StringBuilder sb = new StringBuilder();
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		ArrayDeque<Character> lStack = new ArrayDeque<>(); // 커서 왼쪽에 있는 문자들을 lStack에 저장한다
		ArrayDeque<Character> rStack = new ArrayDeque<>(); // 커서 오른쪽에 있는 문자들을 rStack에 저장한다
		char[] str = br.readLine().toCharArray();
		
		for (int i = 0; i < str.length; i++) {
			lStack.addLast(str[i]); // 초기에는 커서가 문자열의 가장 오른쪽에 있으므로 모든 문자를 lStack에 넣는다
		}
		
		int N = Integer.parseInt(br.readLine());
		
		for (int i = 0; i < N; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			char temp = st.nextToken().charAt(0);
			
			switch (temp) {
			case 'L': // L이 들어오면 커서를 왼쪽으로 한 칸 옮긴다
//				커서 왼쪽에 있는 문자 개수가 줄고, 커서 오른쪽에 있는 문자 개수가 하나 느는것과 같다
//				lStack의 원소를 rStack으로 하나 옮겨준다
				if (!lStack.isEmpty()) rStack.addLast(lStack.pollLast());
				break;
			case 'D': // D가 들어오면 커서를 오른쪽으로 한 칸 옮긴다
//				커서 오른쪽에 있는 문자 개수가 줄고, 커서 왼쪽에 있는 문자 개수가 하나 느는것과 같다
//				rStack의 원소를 lStack으로 하나 옮겨준다
				if (!rStack.isEmpty()) lStack.addLast(rStack.pollLast());
				break;
			case 'B': // B가 들어오면 커서 왼쪽에 있는 문자가 하나 없어진다
				if (!lStack.isEmpty()) lStack.pollLast(); // lStack에서 원소를 하나 제거해준다
				break;
			case 'P': // P가 들어오면 다음 오는 문자를 커서 왼쪽에 붙인다
				char temp2 = st.nextToken().charAt(0);
				lStack.addLast(temp2); // 다음 오는 문자를 lStack에 하나 넣어준다
				break;
			}
		}
		
		
//		lStack의 모든 문자를 rStack으로 옮긴 후, rStack의 모든 문자를 pop() 하면서 출력해주면 문자열을 출력할 수 있다
		while (!lStack.isEmpty()) {
			rStack.addLast(lStack.pollLast());
		}
		
		while (!rStack.isEmpty()) {
			sb.append(rStack.pollLast());
		}
		
		System.out.print(sb.toString());
	} // end of main
} // end of class

 

2개의 스택 - 파이썬

import sys
input = sys.stdin.readline

# 메모리 38664KB / 시간 620ms

l_stack = list(map(str, input().rstrip()))
r_stack = []
N = int(input())

for i in range(N):
    command = input().split()
    if command[0] == "L":
        if len(l_stack) != 0:
            r_stack.append(l_stack.pop())
    elif command[0] == "D":
        if len(r_stack) != 0:
            l_stack.append(r_stack.pop())
    elif command[0] == "B":
        if len(l_stack) != 0:
            l_stack.pop()
    elif command[0] == "P":
        l_stack.append(command[1])

while len(l_stack) > 0:
    r_stack.append(l_stack.pop())

while len(r_stack) > 0:
    print(r_stack.pop(), end="")

 

링크드 리스트 - 자바

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.StringTokenizer;

public class Main { // 메모리 118980KB / 시간 624ms
	static StringBuilder sb = new StringBuilder();
	
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		char[] str = br.readLine().toCharArray();
		int N = str.length;
		LinkedList<Character> ll = new LinkedList<>();
		
		for (int i = 0 ; i < N; i++) {
			ll.add(str[i]);
		}
		
		ListIterator<Character> iter = ll.listIterator(); // 리스트에서 커서 위치를 저장할 ListIterator을 만든다
		
		while (iter.hasNext()) { // 
			iter.next(); // iter에 대입해줄 필요 없이 알아서 iter를 다음으로 넘겨줌
		}
		
		int M = Integer.parseInt(br.readLine());
		
		for (int i = 0; i < M; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			char temp = st.nextToken().charAt(0);
			switch (temp) {
			case 'L':
				if (iter.hasPrevious()) iter.previous(); // 링크드리스트에 이전 노드가 있으면 그 노드를 가리키도록 함
				break;
			case 'D':
				if (iter.hasNext()) iter.next(); // 링크드리스트에 다음 노드가 있으면 그 노드를 가리키도록 함
				break;
			case 'B':
				if (iter.hasPrevious()) { // 링크드 리스트에 이전 노드가 있으면 그 노드를 가리킨 뒤 지움
					iter.previous();
					iter.remove();
				}
				break;
			case 'P':
				char temp2 = st.nextToken().charAt(0);
				iter.add(temp2); // 링크드의 현재 가리키고 있는 노드의 index에 temp2가 오도록 추가함
				break;
			}
		}
		
		for (char c : ll) {
			sb.append(c);
		}
		
		System.out.print(sb.toString());
		
	} // end of main
} // end of class

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

 

2304번: 창고 다각형

첫 줄에는 기둥의 개수를 나타내는 정수 N이 주어진다. N은 1 이상 1,000 이하이다. 그 다음 N 개의 줄에는 각 줄에 각 기둥의 왼쪽 면의 위치를 나타내는 정수 L과 높이를 나타내는 정수 H가 한 개의

www.acmicpc.net


풀이 과정

지붕의 수평 부분이 어떤 기둥의 윗면, 지붕의 수직 부분이 어떤 기둥의 옆면과 닿아야 하고, 지붕에 오목한 부분이 있으면 안될 때 창고 다각형의 최소 값을 구하는 문제이다.

 

지붕의 높이가 높았다가, 낮았다가, 다시 높아지는 경우에 지붕에 오목한 부분이 생기게 된다. 막대의 높이가 가장 높은 곳을 T라 했을 때, 지붕의 시작점부터 T까지는 지붕의 높이가 높아지기만 해야 하고, T부터 지붕의 끝까지는 지붕의 높이가 낮아지기만 해야 한다. 그렇지 않으면 오목한 부분이 생기게 될 것이다.

 

막대의 길이를 쭉 입력 받고, 막대의 높이가 가장 높은 곳 T를 기준으로 반씩 나눠서, 지붕의 시작부터 T까지는 지붕의 높이가 점점 높아지게, T부터 지붕의 끝 까지는 지붕의 높이가 점점 낮아지게끔 막대를 보고 지붕의 높이를 설정해주면 지붕에 오목한 부분이 생기지 않게끔 창고 다각형의 최소 값을 구할 수 있다.


소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main { // 메모리 11980kb / 시간 84ms
    static StringBuilder sb = new StringBuilder();
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());

        // 가장 큰 막대를 기준으로 절반 나눠서 진행할 것이므로 가장 높은 막대의 위치, 막대가 처음 나오는 위치, 막대가 마지막으로
        // 나오는 위치를 저장할 변수들을 선언하였다
        int maxHeight = Integer.MIN_VALUE;
        int maxSpace = Integer.MIN_VALUE;
        int finalSpace = Integer.MIN_VALUE;
        int startSpace = Integer.MAX_VALUE;

        int[] board = new int[1001]; // 4kb정도?  L이 1000이하까지 들어오므로 인덱스 편하게 쓰려고 1001까지 잡았다

        for (int i = 0; i < N; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int L = Integer.parseInt(st.nextToken());
            int H = Integer.parseInt(st.nextToken());
            board[L] = H; // 보드에 저장

            if (maxHeight < H) { // 가장 높은 막대의 높이와 위치를 저장한다
                maxHeight = H;
                maxSpace = L;
            }

            startSpace = Math.min(L, startSpace); // 막대가 시작되는 위치를 구한다
            finalSpace = Math.max(L, finalSpace); // 막대가 끝나는 위치를 구한다
        }

        int maxTemp = Integer.MIN_VALUE;
        
        // 일단 가장 긴 막대 길이를 더한다
        int answer = maxHeight;

        // 시작지점부터 높은 막대 위치 직전까지 점검하면서 점검한 막대 길이중 가장 긴 길이를 더해 나간다
        for (int i = startSpace; i < maxSpace; i++) {
            maxTemp = Math.max(board[i], maxTemp);
            answer += maxTemp;
        }

        maxTemp = Integer.MIN_VALUE;
        
        // 끝 지점부터 높은 막대 위치 직후까지 점검하면서 점검한 막대 길이중 가장 긴 길이를 더해 나간다
        for (int i = finalSpace; i > maxSpace; i--) {
            maxTemp = Math.max(board[i], maxTemp);
            answer += maxTemp;
        }

        System.out.println(answer);
    } // end of main
}// end of class

 

import sys
input = sys.stdin.readline

# 메모리 31256kb / 시간 44ms

N = int(input())
board = [0 for _ in range(1001)]
maxSpace = 0
maxHeight = 0
startSpace = 1001
finalSpace = -1

for i in range(N):
    L, H = map(int, input().split())
    board[L] = H
    startSpace = min(startSpace, L)
    finalSpace = max(finalSpace, L)

    if maxHeight < H:
        maxHeight = H
        maxSpace = L

answer = maxHeight
max_temp = 0

for i in range(startSpace, maxSpace):
    max_temp = max(max_temp, board[i])
    answer += max_temp

max_temp = 0

for i in range(finalSpace, maxSpace, -1):
    max_temp = max(max_temp, board[i])
    answer += max_temp

print(answer)

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

 

10972번: 다음 순열

첫째 줄에 입력으로 주어진 순열의 다음에 오는 순열을 출력한다. 만약, 사전순으로 마지막에 오는 순열인 경우에는 -1을 출력한다.

www.acmicpc.net


풀이 과정

순열이 주어졌을 때, 사전순으로 다음에 오는 순열을 구하는 문제이다.

 

C++에서는 next_permutation 함수를 통해 다음 순열을 구할 수 있지만, 자바와 파이썬에서는 다음 순열을 구하는 내장 함수가 존재하지 않으므로 직접 다음 순열을 구하는 알고리즘을 구현해줘야 한다.

 

다음 순열을 구하는 알고리즘은 다음과 같은 4단계로 이루어진다.

1. a[i-1] < a[i]인 가장 큰 i를 찾는다.

2. a[i-1] < a[j]인 가장 큰 j를 찾는다.

3. a[i-1]과 a[j]를 교체한다

4. a[i]부터 끝까지 뒤집어준다

 

C++에서는 swap함수가 있고 파이썬은 한 줄로 스왑이 가능한데 함수를 따로 만들거나 보조 변수를 하나 두거나 해야 해서 코드가 좀 길어지는 감이 있는 것 같다.


소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {
	static StringBuilder sb = new StringBuilder();
	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int N = Integer.parseInt(br.readLine());
		int[] arr = new int[N];
		StringTokenizer st = new StringTokenizer(br.readLine());
		
		for (int i = 0; i < N; i++) {
			arr[i] = Integer.parseInt(st.nextToken());
		}
		
		int a = N-1, b = N-1;
		
		while (a > 0 && arr[a-1] > arr[a]) { // arr[i-1] < a[i]인 가장 큰 i를 탐색한다
			a--;
		}
		
		if (a == 0) {
			System.out.println(-1);
			return;
		}
		
		while (b > 0 && arr[a-1] > arr[b]) { // arr[i-1] < a[j]인 가장 큰 j를 탐색한다
			b--;
		}
		
// arr[i-1]과 a[j]의 값을 바꾼다
		int temp = arr[a-1];
		arr[a-1] = arr[b];
		arr[b] = temp;
		b = N-1;
		
// arr[i]부터 모든 순서를 뒤집는다
		while (a < b) {
			temp = arr[a];
			arr[a] = arr[b];
			arr[b] = temp;
			a++; b--;
		}
		
		
		
		for (int i = 0; i < N; i++) {
			sb.append(arr[i]).append(" ");
		}
		
		System.out.print(sb.toString());
	} // end of main
} // end of class

 

import sys
input = sys.stdin.readline

N = int(input())
a = list(map(int, input().split()))
i = len(a) - 1

while i > 0 and a[i-1] >= a[i]:  # a[i-1] < a[i]인 가장 큰 i를 찾는다
    i -= 1

if i <= 0:
    print(-1)
    exit(0)

j = len(a) - 1

while a[i-1] >= a[j]:  # a[i-1] < a[j]인 가장 큰 j를 찾는다
    j -= 1

a[i-1], a[j] = a[j], a[i-1]  # a[i-1]과 a[j]를 교체한다
j = len(a) - 1

while i < j:  # a[i]부터 끝까지 순열을 뒤집는다
    a[i], a[j] = a[j], a[i]
    i += 1
    j -= 1

print(' '.join(map(str, a)))  # a를 출력한다

 

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV140YnqAIECFAYD 

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com


풀이 과정

완전 이진 트리가 주어질 때 중위 순회의 결과값을 출력하는 문제이다.

 

완전 이진 트리의 루트를 1번 노드라고 하면, 배열로 트리를 표현할 수 있다.

i번째 노드의 왼쪽 자식 노드는 2i, 오른쪽 자식 노드는 2i+1, 부모 노드는 i/2로 저장하면 배열로 트리의 표현이 가능하다.

 

전위 순회는 자신 -> 왼쪽 자식 -> 오른쪽 자식

중위 순회는 왼쪽 자식 -> 자신 -> 오른쪽 자식

후위 순회는 왼쪽 자식 -> 오른쪽 자식 -> 자신

 

순으로 순회한다. 보통 전, 중, 후위 순회를 구현할 때 재귀를 이용하는데, 재귀문의 위치를 조금만 바꿔주면 3가지의 순회를 쉽게 구현할 수 있다.


소스 코드

import sys
input = sys.stdin.readline


def dfs(cur):
    if cur > n: # 트리 크기보다 현재 방문점이 커지면 종료
        return
    dfs(cur*2) # 왼쪽 자식 노드 방문
    print(temp[cur][1], end="") # 현재 노드 출력
    dfs(cur*2+1) # 오른쪽 자식 노드 방문
    #왼쪽 -> 현재 -> 오른쪽 순으로 방문하는 중위 순회이며 위의 3줄의 코드의 위치에 따라
    #전위, 중위, 후위 순회가 결정된다


for tc in range(1, 11):
    n = int(input())
    temp = [0] # 0번 노드는 안쓸거니까 비워둔다

    for i in range(n):
        temp.append(list(map(str, input().split()))) # 입력

    print(f"#{tc} ", end="")
    dfs(1) # 1번 노드부터 재귀에 들어간다
    print()

 

https://swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AXRSXf_a9qsDFAXS 

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com


풀이 과정

N과 M이 주어질 때, M의 이진수 표현의 마지막 비트 N자리가 모두 1인지를 판별하는 문제이다.

 

N자리가 모두 1로 차있는 이진수 T를 고려해보자. M과 T를 OR 연산을 시행했을때, M의 마지막 비트 N자리중에 0인 비트는 1로 바뀌게 되어 M은 다른 숫자로 변하게 된다. 따라서 N자리가 모두 1일때만 OR 연산을 시행 후에도 M의 값이 변하지 않게 됨을 이용해 비트마스킹으로 문제를 해결할 수 있다.


소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Solution {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int T = Integer.parseInt(br.readLine());

        for (int tc = 1; tc <= T; tc++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int N = Integer.parseInt(st.nextToken());
            int M = Integer.parseInt(st.nextToken());
            if ((M | ((1 << N)) - 1) == M) { // M과, 마지막 N비트에 1을 채운 수를 OR 연산한 값이 M이면, M의 마지막 N비트는 모두 1이다. 
                // 마지막 N비트에 1이 아닌 수가 있다면, OR 연산 이후에 1로 바뀌고, 기존의 M과 다른 수가 된다.
                System.out.printf("#%d %s\n", tc, "ON"); // 따라서 ON이다.
            } else {
                System.out.printf("#%d %s\n", tc, "OFF"); // 아니면 OFF다.
            }
        } // end of testcase
    } // end of main
} // end of class

 

+ Recent posts