for (int i = 0; i < numbers.length; i++) {
			for (int j = i + 1; j < numbers.length; j++) {
				if (numbers[i] < numbers[j]) {
					numbers[i] = numbers[j];
					break;
				}
			}
		}

처음에는 위와 같이 이중 for문을 사용하여 풀었다.

하지만 시간복잡도는 O(N^2) (N = 1,000,000) 로 당연히 시간 초과가 났다.

 

아래 코드와 같이 우선 순위 큐를 사용하여, 문제를 풀 수 있었다.

유사한 문제로는 백준 알고리즘의 [오큰수] 라는 문제가 있다. 

 

다른 블로그의 풀이를 참고하였고, 예전에 백준에서 푼 기억이 있는데 도저히 방법이 생각 안났다.

import java.util.PriorityQueue;

class Solution {
	public int[] solution(int[] numbers) {
		
		// 작은 숫자부터 큐 우선순위로 넣는다
		PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> a[1] - b[1]);

		for (int i = 0; i < numbers.length; i++) {
			int v = numbers[i];

			// 큐가 비어있지 않고, 큐에서 꺼난 값이 현재 인덱스의 값보다 작으면 실행
			// 큐에 들어있다는 것은 앞순서에서 넣었고 아직 정해지지 않은 것
			// 현재 인덱스의 값과 현재 큐에 들어있는 값을 비교한다
			while (!q.isEmpty() && q.peek()[1] < v) {
				// Queue에 넣은 numbers의 값들은 이미 사용했기 때문에
				// 값을 변경해도 상관없다
				numbers[q.poll()[0]] = v;
			}
			
			// 큐에 넣는다
			q.add(new int[] { i, v });
		}

		// 아직도 큐에 남아있다는 것은 오른쪽에 자기보다 큰 수가 없기 때문에
		// -1로 값 지정
		while (!q.isEmpty()) {
			numbers[q.poll()[0]] = -1;
		}

		return numbers;
	}
}

 

 

유사 문제

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

 

17298번: 오큰수

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 수열 A의 원소 A1, A2, ..., AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.

www.acmicpc.net

 

Lv.3 억억단을 외우자 : 프로그래머스

문제 설명

영우는 천하제일 암산대회를 앞두고 있습니다. 암산보다는 암기에 일가견이 있는 영우는 구구단을 확장하여 억억단을 만들고 외워버리기로 하였습니다.
억억단은 1억 x 1억 크기의 행렬입니다. 억억단을 외우던 영우는 친구 수연에게 퀴즈를 내달라고 부탁하였습니다.
수연은 평범하게 문제를 내봐야 영우가 너무 쉽게 맞히기 때문에 좀 어렵게 퀴즈를 내보려고 합니다. 적당한 수 e를 먼저 정하여 알려주고 e 이하의 임의의 수 s를 여러 개 얘기합니다. 영우는 각 s에 대해서 s보다 크거나 같고 e 보다 작거나 같은 수 중에서 억억단에서 가장 많이 등장한 수를 답해야 합니다. 만약 가장 많이 등장한 수가 여러 개라면 그 중 가장 작은 수를 답해야 합니다.
수연은 영우가 정답을 말하는지 확인하기 위해 당신에게 프로그램 제작을 의뢰하였습니다. e와 s의 목록 starts가 매개변수로 주어질 때 각 퀴즈의 답 목록을 return 하도록 solution 함수를 완성해주세요.

제한 사항

  • 1 ≤ e ≤ 5,000,000
  • 1 ≤ starts의 길이 ≤ min {e,100,000}
  • 1 ≤ starts의 원소 ≤ e
  • starts에는 중복되는 원소가 존재하지 않습니다.

분석

억억단에서 s 보다 같거나 크고, e 보다 같거나 작은 수를 구하고, 그 중 가장 많이 등장한 수를 구하여야 하고, 등장 수치가 같으면 작은 값을 선택한다.

풀이

  • e의 범위가 5,000,000 이기 때문에 억억단의 나올 수 있는 가장 큰 수치는 5,000,000 ^ 2이다. 하지만 5,000,000 초과된 값은 우리가 찾아야하는 값이 아님
  • 시간을 조금 줄이기 위해 값을 찾을 때 억억단 표의 반만 사용하였음. i * j 를 확인하면서, i == j인 경우 1을 카운트, i != j 인 경우, i * j, j * i 두 가지 방식이 나오기 때문에 2를 카운트
  • Map(값, 등장 수치)을 구한 뒤, List에 넣어 등장 수치가 많은 순 (같다면 값이 작은 순)으로 정렬
  • starts 배열 값을 확인하면서 List index 0 부터 확인하면서, 값이 s 이상인 것을 찾음

 

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

class Solution {
    public int[] solution(int e, int[] starts) {
        int size = starts.length;
        int[] answer = new int[size];

        Map<Integer, Integer> map = new HashMap<Integer, Integer>();

        for (int i = 1; i <= e; i++) {
            for (int j = 1; j <= i; j++) {
                long value = i * j;
                if (e < value)
                    break;
                if (i == j) {
                    map.put(i * j, map.getOrDefault(i * j, 0) + 1);
                } else {
                    map.put(i * j, map.getOrDefault(i * j, 0) + 2);
                }
            }
        }

        ArrayList<int[]> list = new ArrayList<int[]>();
        for (int key : map.keySet()) {
            list.add(new int[] { key, map.get(key) });
        }

        list.sort(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[1] == o2[1])
                    return o1[0] - o2[0];
                return o2[1] - o1[1];
            }
        });

        for (int a = 0; a < size; a++) {
            for (int i = 0; i < list.size(); i++) {
                if (starts[a] <= list.get(i)[0]) {
                    answer[a] = list.get(i)[0];
                    break;
                }
            }
        }

        return answer;
    }
}

Union-Find (유니온 파인드)란?

  • 그래프 알고리즘으로 합집합 알고리즘이다
  • 상호 배타적 집합(Disjoint-set)이라고도 한다.
    ※ Disjoint set: 서로 중복되지 않는 부분 집합들로 나눠진 원소들에대한 정보를 저장하고 조작하는 자료구조

Union-Find (유니온 파인드) 특징

  • 집합을 구현하는 데는 배열, 리스트 등을 이용할 수 있으나 그 중 가장 효율적인 트리 구조를 이용하여 구현
  • 두 노드가 서로 같은 그래프에 속하는지 판별하는 알고리즘

알고리즘 문제

백준 1976 여행가자

동혁이는 친구들과 함께 여행을 가려고 한다. 한국에는 도시가 N개 있고 임의의 두 도시 사이에 길이 있을 수도, 없을 수도 있다. 동혁이의 여행 일정이 주어졌을 때, 이 여행 경로가 가능한 것인지 알아보자. 물론 중간에 다른 도시를 경유해서 여행을 할 수도 있다. 예를 들어 도시가 5개 있고, A-B, B-C, A-D, B-D, E-A의 길이 있고, 동혁이의 여행 계획이 E C B C D 라면 E-A-B-C-B-C-B-D라는 여행경로를 통해 목적을 달성할 수 있다.

도시들의 개수와 도시들 간의 연결 여부가 주어져 있고, 동혁이의 여행 계획에 속한 도시들이 순서대로 주어졌을 때 가능한지 여부를 판별하는 프로그램을 작성하시오. 같은 도시를 여러 번 방문하는 것도 가능하다.

문제 분석

각 도시들 간의 연결 여부와 여행 계획이 주어지고, 출발지에서 목적지까지 여행 가능 여부를 판별한다.
여행 계획에 포함된 도시들이 같은 그래프에 속해있는지 구한다.

 

    for (int i = 0; i < N; i++)
        parent[i] = i;

각 그래프의 root 노드를 저장하는 parent 배열을 선언하고, 초기 값은 자기 자신을 저장한다.

    static void union(int x, int y) {
        x = find(x);
        y = find(y);
        if (x > y)
            parent[x] = y;
        else
            parent[y] = x;
    }

연결되어 있는 두 노드를 union하여 같은 root를 공유하도록 최신화한다.
두 노드의 root 값 중 작은 값을 root로 저장한다.

    static int find(int x) {
        if (parent[x] == x)
            return x;
        return parent[x] = find(parent[x]);
    }

재귀 함수를 통해 그래프의 root 노드를 찾아 parent 값을 최신화하고, argument와 parent 값이 같을 경우, 루트 노드에 도달하였기 때문에 재귀를 중단한다.

풀이 코드

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

class Main {

    static int N, M;
    static int[][] map;
    static int[] parent;
    static String[] input;

    static int find(int x) {
        if (parent[x] == x)
            return x;
        return parent[x] = find(parent[x]);
    }

    static void union(int x, int y) {
        x = find(x);
        y = find(y);
        if (x > y)
            parent[x] = y;
        else
            parent[y] = x;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());
        M = Integer.parseInt(br.readLine());
        map = new int[N][N];
        parent = new int[N];

        for (int i = 0; i < N; i++)
            parent[i] = i;

        for (int i = 0; i < N; i++) {
            input = br.readLine().split(" ");
            for (int j = 0; j < N; j++) {
                map[i][j] = Integer.parseInt(input[j]);
                if (map[i][j] == 1) {
                    union(i, j);
                }
            }
        }

        boolean isCycle = true;
        input = br.readLine().split(" ");
        for (int i = 0; i < M - 1; i++) {
            int a = Integer.parseInt(input[i]) - 1;
            int b = Integer.parseInt(input[i + 1]) - 1;

            if (parent[a] != parent[b]) {
                isCycle = false;
                break;
            }
        }

        System.out.println(isCycle ? "YES" : "NO");
        br.close();
    }

}

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

 

Spanning Tree

 - 그래프 내의 모든 정점을 포함하는 트리

 - 그래프의 최소 연결 부분 그래프

 - 모든 정점들이 연결되어 있어야 하고, 사이클을 포함해서는 안됨

 - 그래프에 있는 n개의 정점을  n-1 개의 간선으로 연결

 

MST의 특징

 - 간선의 가중치의 합이 최소

 - n개의 정점을 가지고 그래프에 대해 n-1 개의 간선만을 사용

 - 사이클을 포함해서는 안됨

 


문제

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

 

풀이

처음에는  DFS + 백트래킹으로 풀었다가 시간 초과

이 문제는 MST 알고리즘으로 풀어야한다

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

class Main {

	static String[] input;
	static StringBuilder sb = new StringBuilder();
	static int N, C;
	static int[] parent;

	// 유니온 파인드
	// 부모(root) 노드를 찾는 과정
	static int find(int n) {
		if (n == parent[n])
			return n;
		return parent[n] = find(parent[n]);
	}

	// 연결된 두 노드의 부모 노드를 찾고, 하나의 부모 노드로 동기화 시켜준다
	static void union(int x, int y) {
		x = find(x);
		y = find(y);

		// 다른 부모 노드 값이 나온다면, 인덱스 값이 더 작은 노드를 부모 노드로 정한다
		if (x < y)
			parent[y] = x;
		else
			parent[x] = y;
	}

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		input = br.readLine().split(" ");
		N = Integer.parseInt(input[0]);
		C = Integer.parseInt(input[1]);

		Node[] node = new Node[N];
		for (int i = 0; i < N; i++) {
			input = br.readLine().split(" ");

			// Node( x좌표, y좌표, 0) 으로 생각
			node[i] = new Node(Integer.parseInt(input[0]), Integer.parseInt(input[1]), 0);
		}

		// 비용(cost)가 적은
		PriorityQueue<Node> pq = new PriorityQueue<>((n1, n2) -> n1.cost - n2.cost);

		for (int i = 0; i < N; i++) {
			for (int j = i + 1; j < N; j++) {
				int cost = (int) Math.pow(node[i].x - node[j].x, 2) + (int) Math.pow(node[i].y - node[j].y, 2);
				// 노드간 비용이 최소 비용을 넘어야만 (관계를 사용할 수 있기 때문에) 우선순위 큐에 넣는다.
				if (C <= cost) {
					// Node ( i번호 노드, j번호 노드, 비용)
					pq.add(new Node(i, j, cost));
				}
			}
		}

		// 노드들의 부모 노드를 자기 자신으로 초기화
		parent = new int[N];
		for (int i = 0; i < N; i++)
			parent[i] = i;

		int result = 0;
		int count = 0; // 모든 정점을 방문했는지 확인하기 위한 변수

		// 우선순위 큐는 가장 최소 비용을 가진 노드 관계를 우선적으로 뽑는다
		while (!pq.isEmpty()) {
			Node n = pq.poll(); // x번 노드, y번 노드, 비용
			if (find(n.x) == find(n.y)) // 노드 관계가 확인된 것이라면 continue
				continue;

			union(n.x, n.y); // union 합친다
			count += 1; // 1개의 노드 관계를 카운트
			result += n.cost;
		}

		// 노드 개수가 N개라면 관계는 N-1개이다. 위에서 중복되지않고 모든 노드를 차례로 관계를 확인했다면 count == N-1
		System.out.println(count < N - 1 ? -1 : result);
		br.close();
	}

}

class Node {
	int x, y, cost;

	Node(int x, int y, int cost) {
		this.x = x;
		this.y = y;
		this.cost = cost;
	}
}

 

 


참고 사이트

 

[알고리즘] 최소 신장 트리(MST, Minimum Spanning Tree)란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

가을을 맞아 카카오프렌즈는 단체로 소풍을 떠났다. 즐거운 시간을 보내고 마지막에 단체사진을 찍기 위해 카메라 앞에 일렬로 나란히 섰다. 그런데 각자가 원하는 배치가 모두 달라 어떤 순서로 설지 정하는데 시간이 오래 걸렸다. 네오는 프로도와 나란히 서기를 원했고, 튜브가 뿜은 불을 맞은 적이 있던 라이언은 튜브에게서 적어도 세 칸 이상 떨어져서 서기를 원했다. 사진을 찍고 나서 돌아오는 길에, 무지는 모두가 원하는 조건을 만족하면서도 다르게 서는 방법이 있지 않았을까 생각해보게 되었다. 각 프렌즈가 원하는 조건을 입력으로 받았을 때 모든 조건을 만족할 수 있도록 서는 경우의 수를 계산하는 프로그램을 작성해보자.

 

입력 형식

입력은 조건의 개수를 나타내는 정수n과n개의 원소로 구성된 문자열 배열data로 주어진다.data의 원소는 각 프렌즈가 원하는 조건이N~F=0과 같은 형태의 문자열로 구성되어 있다. 제한조건은 아래와 같다.

  • 1 <= n <= 100
  • data의 원소는 다섯 글자로 구성된 문자열이다. 각 원소의 조건은 다음과 같다.
    • 첫 번째 글자와 세 번째 글자는 다음 8개 중 하나이다.{A, C, F, J, M, N, R, T}각각 어피치, 콘, 프로도, 제이지, 무지, 네오, 라이언, 튜브를 의미한다. 첫 번째 글자는 조건을 제시한 프렌즈, 세 번째 글자는 상대방이다. 첫 번째 글자와 세 번째 글자는 항상 다르다.
    • 두 번째 글자는 항상~이다.
    • 네 번째 글자는 다음 3개 중 하나이다.{=, <, >}각각 같음, 미만, 초과를 의미한다.
    • 다섯 번째 글자는0이상6이하의 정수의 문자형이며, 조건에 제시되는 간격을 의미한다. 이때 간격은 두 프렌즈 사이에 있는 다른 프렌즈의 수이다.

출력 형식

모든 조건을 만족하는 경우의 수를 리턴한다.

 

예제 입출력

2 ["N~F=0", "R~T>2"] 3648
2 ["M~C<2", "C~M>1"] 0

예제에 대한 설명

첫 번째 예제는 문제에 설명된 바와 같이, 네오는 프로도와의 간격이 0이기를 원하고 라이언은 튜브와의 간격이 2보다 크기를 원하는 상황이다.

두 번째 예제는 무지가 콘과의 간격이 2보다 작기를 원하고, 반대로 콘은 무지와의 간격이 1보다 크기를 원하는 상황이다. 이는 동시에 만족할 수 없는 조건이므로 경우의 수는 0이다.


모든 조건을 만족하는지 확인하기 위해서는 완전 탐색을 해야한다고 생각했고,

8개의 캐릭터가 줄을 설 수 있는 방법은 8! = 40320 가지가 나와서 충분히 완전 탐색을 하면 되겠다라고 생각했다.

 

DFS(깊이 우선 탐색)으로  순서(01234567 ~ 76543210)를 정하고, 조건들을 확인하고 모든 조건을 만족하면 경우의 수 1을 리턴한다

 

순서를 정할때는 문자열(숫자)로 저장하고, 각 문자(숫자)는  ID 배열의 인덱스이다

입력(조건)을 받을 때는 캐릭터들의 이니셜을 문자 타입으로 받기 때문에, Map 타입을 통해 캐릭터 이니셜과 현재 위치(인덱스)를 매핑해줬다

 

캐릭터들의 위치를 배열로 저장하게 된다면, 입력 조건을 확인할 때마다 2개의 캐릭터의 위치를 확인하기 위해서 O(N)의 시간복잡도를 가지지만, Map 타입을 사용하게 된다면 O(1) 시간복잡도만으로 위치를 찾을 수 있기 때문에 좀 더 효율적이지 않을까 생각했다

 

내 코드 진단 & 생각

 

- 캐릭터들의 순서가 정해졌는지 저장하는 boolean 배열 객체를 dfs 실행마다 생성하여 다음 dfs 함수로 넘기지 않는다. (당연히 메모리 낭비니까?..)   전역변수(boolean 배열 b)로 선언하여 true/false 변경하며 백트래킹을 사용함. 그래도 전역 변수를 사용하지 않고 solution 함수에 boolean 배열 객체를 선언하여 dfs 매개 변수에 추가했으면 좀 더 깔끔한 코드가 되지 않았을까?

 

- Map도 전역 변수로 선언하여, dfs 안에서 객체 생성을 하지 않고, clear()를 통해 재사용했는데 위 생각처럼 dfs 매개 변수에 추가하면 더 좋은 코드가 됐을 것 같다. (ID 배열도 마찬가지).  그런데 dfs 안에서 Map 객체를 생성했더라도 Map 데이터  쌍 ('A',0), ('C',1) 들도 map.put 할 때마다 기본형 타입이 아닌 객체로 저장되니  이러나 저러나 3가지 방식 모두 메모리 상으론 비슷하지 않을까?

 

- 백준 사이트 같았으면  결과값(answer)도 전역으로 선언하고,  void dfs() 해서 풀었을텐데, 프로그래머스는 제출 방식이  다르다보니 int dfs()로 경우의 수를 리턴하여 answer에 더하는 방식으로 풀어봤다. 

 

 

 

import java.util.HashMap;
import java.util.Map;

class Solution {

	char[] ID = { 'A', 'C', 'F', 'J', 'M', 'N', 'R', 'T' };
	boolean[] b = new boolean[8];
	Map<Character, Integer> map = new HashMap<Character, Integer>();

	int dfs(String[] data, int depth, String order) {

		if (depth == 8) {
			map.clear();

			for (int i = 0; i < order.length(); i++) {
				map.put(ID[order.charAt(i) - '0'], i);
			}

			for (int i = 0; i < data.length; i++) {
				int dist = Math.abs(map.get(data[i].charAt(0)) - map.get(data[i].charAt(2))) - 1;
				int cpVal = data[i].charAt(4) - '0';
				char sign = data[i].charAt(3);
				if (sign == '>') {
					if (dist <= cpVal)
						return 0;
				} else if (sign == '<') {
					if (dist >= cpVal)
						return 0;
				} else {
					if (dist != cpVal)
						return 0;
				}
			}

			return 1;
		}

		int ret = 0;

		for (int i = 0; i < 8; i++) {
			if (b[i])
				continue;
			b[i] = true;
			ret += dfs(data, depth + 1, order + i);
			b[i] = false;
		}

		return ret;
	}

	public int solution(int n, String[] data) {
		int answer = 0;

		answer += dfs(data, 0, "");

		return answer;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
 
public class Main {
    // 타잔 알고리즘은 위상 정렬을 이용한 방법으로 생성되는 SCC들은 위상정렬의 역순으로 생성된다.
    // 위상정렬 : 방향성을 거스르지 않게 정점들을 나열하는 알고리즘.
    static int V, E, discover[], scc[], c, r;
    static Stack<Integer> stk = new Stack<Integer>();
    static ArrayList<ArrayList<Integer>> vt = new ArrayList<ArrayList<Integer>>();;
    static ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();;
 
    static int dfs(int here) {
        discover[here] = c++// c는 방문 표시? 0<= c < V
        int ret = discover[here]; // 현재 discover[here]==-1 상태로 이값에 자기의 방문 순서를 넣어준다.
        System.out.println(here+" 진입 , discover = "+ret);
        stk.push(here); // dfs 호출 순서에 따라 정점을 stack에 push
        for (int there : vt.get(here)) // 자신이 탐색할 수 있는 정점 중 가장 높은 위치를 return
            if (discover[there] == -1// 아직 방문하지 않은 점이라면
                ret = Math.min(ret, dfs(there));
            else if (scc[there] == -1// 다음에 갈곳이 아직 scc를 이루지 않았다면
                ret = Math.min(ret, discover[there]);
        }
        if (ret == discover[here]) { // return된 값이 자기에게 할당된 discover와 같다면
            System.out.println(here+"일 때, scc로 묶는다."+" => discover["+here+"] = "+discover[here]+", scc["+here+"] = "+scc[here]+" ret = "+ret);
            ArrayList<Integer> tmp = new ArrayList<Integer>();
            while (true) {
                int t = stk.pop();
                System.out.println("pop = "+t);
                scc[t] = r; // r은 res의 크기(size) scc를 이루었다면 각 정점에 표시
                tmp.add(t);
                if (t == here) // 해당 정점이 나올때까지 pop하면서 모든 정점을 같은 scc로 묶는다.
                    break;
            }
            System.out.println(Arrays.toString(discover));
            System.out.println(Arrays.toString(scc));
            Collections.sort(tmp);
            res.add(tmp);
            r++;
        }
        System.out.println("return => discover["+here+"] = "+discover[here]+", scc["+here+"] = "+scc[here]);
        System.out.println();
        return ret;
    }
 
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        V = Integer.parseInt(st.nextToken());
        E = Integer.parseInt(st.nextToken());
 
        for (int i = 0; i <= V; i++)
            vt.add(new ArrayList<Integer>());
        for (int i = 0, a, b; i < E; i++) {
            st = new StringTokenizer(br.readLine());
            a = Integer.parseInt(st.nextToken());
            b = Integer.parseInt(st.nextToken());
            vt.get(a).add(b); // 간선 정보들을 저장
        }
 
        discover = new int[V + 1];
        scc = new int[V + 1];
        Arrays.fill(discover, -1);
        Arrays.fill(scc, -1);
 
        for (int i = 1; i <= V; i++) {
            if (discover[i] == -1)
                dfs(i);
        }
 
        Collections.sort(res, new Comparator<ArrayList<Integer>>() {
            @Override
            public int compare(ArrayList<Integer> o1, ArrayList<Integer> o2) {
                return o1.get(0- o2.get(0);
            }
        });
 
        StringBuilder sb = new StringBuilder();
        sb.append(r + "\n");
        for (int i = 0; i < r; i++) {
            for (int v : res.get(i))
                sb.append(v + " ");
            sb.append("-1\n");
        }
        System.out.println(sb);
        System.out.println(Arrays.toString(discover));
        System.out.println(Arrays.toString(scc));
        br.close();
    }
 
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4ftext-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none;color:white">cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 
public class Main {
 
    static int C, N;
    static int cache[][] = new int[101][101];
    static String W, S;
 
    static int match(int w, int s) {
        if (cache[w][s] != -1)
            return cache[w][s];
        int ow = w, os = s;
 
        while (s < S.length() && w < W.length() && (W.charAt(w) == '?' || W.charAt(w) == S.charAt(s))) {
            ++w;
            ++s;
        }
        if (w == W.length())
            return cache[ow][os] = (s == S.length() ? 1 : 0);
        if (W.charAt(w) == '*')
            for (int skip = 0; skip + s <= S.length(); ++skip)
                if (match(w + 1, s + skip) == 1)
                    return cache[ow][os] = 1;
        return cache[ow][os] = 0;
    }
 
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        C = Integer.parseInt(br.readLine());
//        StringTokenizer st = null;
 
        ArrayList<String> result = new ArrayList<String>();
//        ArrayList<String> list = new ArrayList<String>();
        StringBuilder sb = new StringBuilder();
 
        for (int c = 0; c < C; c++) {
            W = br.readLine();
            N = Integer.parseInt(br.readLine());
            result.clear();
            
//            list.clear();
            for (int i = 0; i < N; i++) {
                for (int j= 0; j <= 100; j++)
                    Arrays.fill(cache[j], -1);
//                list.add(br.readLine());
                S = br.readLine();
                if(match(00)==1) {
                    result.add(S);
                }
            }
 
            Collections.sort(result);
            for (String word : result) {
                sb.append(word + "\n");
            }
        } // tc end
        System.out.println(sb);
 
        br.close();
    }
 
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

정리해야함 코드도 내용도

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

 

2042번: 구간 합 구하기

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)과 M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000) 가 주어진다. M은 수의 변경이 일어나는 회수이고, K는 구간의 합을 구하는 회수이다. 그리고 둘째 줄부터 N+1번째 줄까지 N개의 수가 주어진다. 그리고 N+2번째 줄부터 N+M+K+1번째 줄까지 세 개의 정수 a, b, c가 주어지는데, a가 1인 경우 b번째 수를 c로 바꾸고 a가 2인 경우에는 b번째 수부터 c번째 수까지의

www.acmicpc.net

 

[JAVA]

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

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n, m, k;
        n = Integer.parseInt(st.nextToken());
        m = Integer.parseInt(st.nextToken());
        k = Integer.parseInt(st.nextToken());
        long a[] = new long[n];
        int h = (int) Math.ceil(Math.log10(n) / Math.log10(2));
        int tree_size = (1 << (h + 1));
        long tree[] = new long[tree_size];
        m += k;
        for (int i = 0; i < n; i++) {
            a[i] = Integer.parseInt(br.readLine());
        }
        init(a, tree, 1, 0, n - 1);

        StringBuilder sb = new StringBuilder();
        while (m-- > 0) {
            int t1, t2, t3;
            st = new StringTokenizer(br.readLine());
            t1 = Integer.parseInt(st.nextToken());
            if (t1 == 1) {
                long t4;
                t2 = Integer.parseInt(st.nextToken());
                t4 = Long.parseLong(st.nextToken());
                t2 -= 1;
                long diff = t4 - a[t2];
                a[t2] = t4;
                update(tree, 1, 0, n - 1, t2, diff);
            } else if (t1 == 2) {
                t2 = Integer.parseInt(st.nextToken());
                t3 = Integer.parseInt(st.nextToken());
                sb.append(sum(tree, 1, 0, n - 1, t2 - 1, t3 - 1) + "\n");
            }
        }
        System.out.println(sb);
        br.close();
    }

    static long init(long[] a, long[] tree, int node, int start, int end) {
        if (start == end)
            return tree[node] = a[start];
        else
            return tree[node] = init(a, tree, node * 2, start, (start + end) / 2)
                    + init(a, tree, node * 2 + 1, (start + end) / 2 + 1, end);
    }

    static void update(long[] tree, int node, int start, int end, int index, long diff) {
        if (index < start || index > end)
            return;
        tree[node] = tree[node] + diff;
        if (start != end) {
            update(tree, node * 2, start, (start + end) / 2, index, diff);
            update(tree, node * 2 + 1, (start + end) / 2 + 1, end, index, diff);
        }
    }

    static long sum(long[] tree, int node, int start, int end, int left, int right) {
        if (left > end || right < start)
            return 0;
        if (left <= start && end <= right)
            return tree[node];
        return sum(tree, node * 2, start, (start + end) / 2, left, right)
                + sum(tree, node * 2 + 1, (start + end) / 2 + 1, end, left, right);
    }

}

 

 

 


 

 

세그먼트 트리 (Segment Tree)

문제 배열 A가 있고, 여기서 다음과 같은 두 연산을 수행해야하는 문제를 생각해봅시다. 구간 l, r (l ≤ r)이 주어졌을 때, A[l] + A[l+1] + ... + A[r-1] + A[r]을 구해서 출력하기 i번째 수를 v로 바꾸기. A[i] = v 수행해야하는 연산은 최대 M번입니다. 세그먼트 트리나 다른 방법을 사용하지 않고 문제를 푼다면, 1번 연산을 수행하는데 O(N), 2번 연산을 수행하는데 O(1)이 걸리게 됩니다. 총 시간 복잡도는 O

www.acmicpc.net

 

'알고리즘 ' 카테고리의 다른 글

SCC - Tarjan (타잔 알고리즘)  (0) 2019.07.11
[알고스팟] WILDCARD - JAVA  (0) 2019.06.06
백준 1941 : 소문난 칠공주  (0) 2019.05.21
SCC - kosaraju  (0) 2019.05.21
Union-Find (유니온 파인드)  (0) 2019.05.17

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

 

1941번: 소문난 칠공주

총 25명의 여학생들로 이루어진 여학생반은 5*5의 정사각형 격자 형태로 자리가 배치되었고, 얼마 지나지 않아 이다솜과 임도연이라는 두 학생이 두각을 나타내며 다른 학생들을 휘어잡기 시작했다. 곧 모든 여학생이 ‘이다솜파’와 ‘임도연파’의 두 파로 갈라지게 되었으며, 얼마 지나지 않아 ‘임도연파’가 세력을 확장시키며 ‘이다솜파’를 위협하기 시작했다. 위기의식을 느낀 ‘이다솜파’의 학생들은 과감히 현재의 체제를 포기하고, ‘소문난 칠공주’를 결성하는 것이

www.acmicpc.net

[JAVA]
public class Main {
    static char[][] arr = new char[5][5];
    static int[] dy = { -1, 0, 0, 1 }, dx = { 0, -1, 1, 0 };
    static boolean[] b = new boolean[25];
    static int ret = 0;

    static boolean find(int y, int x) {
        boolean[][] check = new boolean[5][5];
        Queue<int[]> q = new LinkedList<>();
        check[y][x] = true;
        int vcnt = 1;
        q.offer(new int[] { y, x });
        while (!q.isEmpty()) {
            int[] s = q.poll();

            for (int i = 0; i < 4; i++) {
                int yy = s[0] + dy[i];
                int xx = s[1] + dx[i];
                if (0 <= yy && yy < 5 && 0 <= xx && xx < 5 && !check[yy][xx] && b[yy * 5 + xx]) {
                    vcnt += 1;
                    check[yy][xx] = true;
                    q.offer(new int[] { yy, xx });
                }
            }
        }
        return vcnt == 7 ? true : false;
    }

    static void dfs(int n, int pick, int sc) {
        sc += arr[n / 5][n % 5] == 'S' ? 1 : 0;
        b[n] = true;
        if (pick == 7) {
            if (sc >= 4) {
                if (find(n / 5, n % 5)) {
                    ret += 1;
                }
            }
        } else {
            for (int i = n + 1; i < 25; i++) {
                if (!b[i])
                    dfs(i, pick + 1, sc);
            }
        }
        b[n] = false;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        for (int i = 0; i < 5; i++) {
            String in = br.readLine();
            for (int j = 0; j < 5; j++) {
                arr[i][j] = in.charAt(j);
            }
        }

        for (int i = 0; i < 25; i++) {
            dfs(i, 1, 0);
        }

        System.out.println(ret);

        br.close();
    }
}

 

[C++]

 
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

char arr[5][5];
bool b[25];
bool check[5][5];
int ret;
queue<pair<int, int>> q;
int dy[]{-1, 0, 0, 1}, dx[]{0, -1, 1, 0};

bool isFriends(int n)
{
    int y = n / 5;
    int x = n % 5;

    for (int i = 0; i < 5; i++)
    {
        fill(check[i], check[i] + 5, false);
    }
    int cnt = 1;
    check[y][x] = true;
    q.push(make_pair(y, x));
    while (!q.empty())
    {
        pair<int, int> p = q.front();
        q.pop();

        for (int i = 0; i < 4; i++)
        {
            y = p.first + dy[i];
            x = p.second + dx[i];
            if (0 <= y && y < 5 && 0 <= x && x < 5 && b[5 * y + x] && !check[y][x])
            {
                cnt++;
                check[y][x] = true;
                q.push(make_pair(y, x));
            }
        }
    }
    return cnt == 7 ? true : false;
}

void combine(int n, int pick, int s)
{
    b[n] = true;
    s += arr[n / 5][n % 5] == 'S' ? 1 : 0;
    if (pick == 7)
    {
        if (s >= 4 && isFriends(n))
            ret++;
    }
    else
    {
        for (int i = n + 1; i < 25; i++)
            combine(i, pick + 1, s);
    }
    b[n] = false;
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    for (int i = 0; i < 5; i++)
        for (int j = 0; j < 5; j++)
            cin >> arr[i][j];

    for (int i = 0; i < 25; i++)
        combine(i, 1, 0);

    cout << ret;
    return 0;
}
 

 

조합을 통해서 7명을 뽑고 이다솜(S)파가 4명이상일 경우에만 7명이 인접해있는지 확인한다.

 


처음 풀때는 DFS를 통해서 7명을 찾을려고 해서 풀지 못했다. 옆 행렬인 경우 (2,1)에서 시작해서 (4,2)와 (4,5)를 포함하는 경우의 수가 존재한다. (4,5) 경우에는 DFS로 탐색이 가능하지만 (4,2) 경로를 포함한 경우는 (2,2) 좌표에서 십자가모양으로 퍼지기 때문에 DFS로 탐색할 수 없다. 

그래서 조합을 통해서 25개의 좌표 중에서 7개를 먼저 선택하는 방식으로 문제를 풀 수 있었다.

'알고리즘 ' 카테고리의 다른 글

[알고스팟] WILDCARD - JAVA  (0) 2019.06.06
백준 2042 : 구간 합 구하기 - 세그먼트 트리  (0) 2019.05.25
SCC - kosaraju  (0) 2019.05.21
Union-Find (유니온 파인드)  (0) 2019.05.17
Parametric search (파라메트릭 서치)  (0) 2019.05.16

SCC ( Strongly Connected Component )

 

강한 연결 요소, 집합 내에서 서로 왕복 가능한 최대 집합의 크기


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

 

2150번: Strongly Connected Component

첫째 줄에 두 정수 V(1≤V≤10,000), E(1≤E≤100,000)가 주어진다. 이는 그래프가 V개의 정점과 E개의 간선으로 이루어져 있다는 의미이다. 다음 E개의 줄에는 간선에 대한 정보를 나타내는 두 정수 A, B가 주어진다. 이는 A번 정점과 B번 정점이 연결되어 있다는 의미이다. 이때 방향은 A->B가 된다.

www.acmicpc.net

 

  1. 방향 그래프, 역방향 그래프, 스택을 준비한다.
  2. 방향 그래프를 사용하여 dfs를 수행하고, 끝나는 순서대로 스택에 push한다. (단, 모든 정점에 대해서 수행)
  3. 스택을 비울 때까지 pop을 하면서, 역방향 그래프를 사용하여 dfs를 수행하고 모든 정점들을 같은 SCC로 묶는다.

[ JAVA ]

class Main {
    static ArrayList<Integer> graph[];
    static ArrayList<Integer> reverse[];
    static boolean visited[];
    static Stack<Integer> st = new Stack<>();
    // Result
    static ArrayList<ArrayList<Integer>> a = new ArrayList<>();

    static void dfs(int now) {
        for (int a : graph[now]) {
            if (visited[a] == false) {
                visited[a] = true;
                dfs(a);
            }
        }
        st.push(now);
    }

    static void rdfs(int base, ArrayList<Integer> temp) {
        temp.add(base);
        for (int a : reverse[base]) {
            if (!visited[a]) {
                visited[a] = true;
                rdfs(a, temp);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer stk = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(stk.nextToken());
        int m = Integer.parseInt(stk.nextToken());

        graph = new ArrayList[n + 1];
        reverse = new ArrayList[n + 1];
        visited = new boolean[n + 1];

        for (int i = 1; i <= n; i++) {
            graph[i] = new ArrayList<>();
            reverse[i] = new ArrayList<>();
        }

        for (int i = 0; i < m; i++) {
            stk = new StringTokenizer(br.readLine());
            int from = Integer.parseInt(stk.nextToken());
            int to = Integer.parseInt(stk.nextToken());
            graph[from].add(to);
            reverse[to].add(from);
        }

        // stack
        for (int i = 1; i <= n; i++) {
            if (!visited[i]) {
                visited[i] = true;
                dfs(i);
            }
        }

        Arrays.fill(visited, false);

        while (!st.isEmpty()) {
            int base = st.pop();
            if (!visited[base]) {
                visited[base] = true;
                ArrayList<Integer> temp = new ArrayList<>();
                rdfs(base, temp);
                Collections.sort(temp);
                temp.add(-1);
                a.add(temp);
            }
        }

        Collections.sort(a, new Comparator<ArrayList<Integer>>() {
            @Override
            public int compare(ArrayList<Integer> o1, ArrayList<Integer> o2) {
                return Integer.compare(o1.get(0), o2.get(0));
            }
        });

        StringBuilder sb = new StringBuilder();
        sb.append(a.size()).append("\n");
        for (int i = 0; i < a.size(); i++) {
            int size = a.get(i).size();
            for (int j = 0; j < size; j++) {
                sb.append(a.get(i).get(j)).append(" ");
            }
            sb.append("\n");
        }
        System.out.println(sb);

        br.close();
    }
}

 

[ C++ ]

#include <stdio.h>
#include <memory.h>
#include <algorithm>
#include <vector>
using namespace std;

typedef vector<int> vi;
const int v_ = 1e4 + 1;

int v, e, cnt, pos, stk[v_];
bool vst[v_];
vi gp1[v_], gp2[v_];
vector<vi> res;

void dfs(int now)
{
    vst[now] = true;
    for (int nxt : gp1[now])
        if (!vst[nxt])
            dfs(nxt);
    stk[pos++] = now;
}

void scc(int now)
{
    vst[now] = true;
    res[cnt].push_back(now);
    for (int nxt : gp2[now])
        if (!vst[nxt])
            scc(nxt);
}

int main()
{
    int i;
    scanf("%d %d", &v, &e);
    for (i = 1; i <= e; i++)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        gp1[a].push_back(b);
        gp2[b].push_back(a);
    }

    for (i = 1; i <= v; i++)
        if (!vst[i])
            dfs(i);

    memset(vst, 0, sizeof(vst));
    while (pos)
    {
        int top = stk[--pos];
        if (vst[top])
            continue;
        res.push_back(vi());
        scc(top);
        cnt++;
    }

    for (i = 0; i < cnt; i++)
        sort(res[i].begin(), res[i].end());
    sort(res.begin(), res.end(), [](const vi &i, const vi &j) { return i[0] < j[0]; });

    printf("%d\n", res.size());

    for (auto &i : res)
    {
        for (auto j : i)
            printf("%d ", j);
        puts("-1");
    }
    return 0;
}
 

 

https://jason9319.tistory.com/98

 

SCC(Strongly Connected Component)

방향 그래프에서 어떤 그룹 X에 있는 임의의 두 정점 A,B에 대해서 항상 A->B로 가는 경로가 존재한다면 그 그룹을 SCC(Strongly Connected Component)라 칭합니다. 더 정확하게 정의하자면 SCC가 되는 그룹은 항..

jason9319.tistory.com

+ Recent posts