반응형

 

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

 

18111번: 마인크래프트

팀 레드시프트는 대회 준비를 하다가 지루해져서 샌드박스 게임인 ‘마인크래프트’를 켰다. 마인크래프트는 1 × 1 × 1(세로, 가로, 높이) 크기의 블록들로 이루어진 3차원 세계에서 자유롭게

www.acmicpc.net

 

 

구현 문제다.

가장 높은 높이부터 0까지 위에서 아래로 차례대로 계산해주는 함수를 호출하며 내려갔다.

로직은 간단한데 구현이 오래 걸린다.

 

채울 때는 시간을 1초 추가해주는 것도 있지만,

박스가 충분한지 확인해줘야 한다.

 

출력은 가장 높은 높이의 값을 출력해줘야 해서 우선순위 큐를 사용했다.

 

 


코드

 

import java.io.*;
import java.math.BigInteger; // subtract , multiply, add, mod
import java.util.*;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    static StringTokenizer st;
    static StringBuilder sb = new StringBuilder();

    static int[][] arr;
    static int[] temp;
    static int globalMin = Integer.MAX_VALUE;
    static int globalMax = Integer.MIN_VALUE;
    static int[] su;

    public static void main(String[] args) throws IOException {

        // 입력부
        StringTokenizer st = new StringTokenizer(br.readLine());
        int low = Integer.parseInt(st.nextToken());
        int col = Integer.parseInt(st.nextToken());
        int boxes = Integer.parseInt(st.nextToken());

        int maxHeight = 0;
        arr = new int[low][col];
        for(int i = 0 ; i<low; i++){
            st = new StringTokenizer(br.readLine(), " ");
            for(int j = 0; j<col; j++ ) {
                int num = Integer.parseInt(st.nextToken());
                arr[i][j] = num;
                maxHeight = Math.max(maxHeight, num);
            }
        }

        int ans = Integer.MAX_VALUE;
        Queue<HB> q = new PriorityQueue<HB>((o1, o2)->{
            if( o1.time == o2.time)
                return o2.height - o1.height;
            return o1.time - o2.time;
        });

        for(int i = maxHeight; i>=0; i--){
            int[] answer = findTime(i, boxes, arr);
            q.offer(new HB(answer[0], answer[1]));
        }
        System.out.println(q.poll());


    }
    public static int[] findTime(int height, int boxes, int[][] arr){
        int[] ans = new int[2];
        int time = 0;
        // 제거시간
        for(int i = 0 ; i<arr.length; i++){
            for(int j = 0; j<arr[0].length; j++){
                if(arr[i][j] > height){
                    int tt = arr[i][j] - height;
                    time += tt * 2; // 제거 2초
                    boxes += tt;
                }
            }
        }

        // 채우기 시간
        for(int i = 0 ; i<arr.length; i++){
            for(int j = 0; j<arr[0].length; j++){
                if(arr[i][j] < height){
                    int tt = height - arr[i][j];
                    time += tt; // 설치 1초
                    boxes -= tt;
                    if(boxes < 0) {
                        ans[0] =Integer.MAX_VALUE;
                        return ans;
                    }
                }
            }
        }
        ans[0] = time;
        ans[1] = height;
        return ans;
    }
}

class HB{
    int time;
    int height;
    public HB(int time, int height){
        this.time = time;
        this.height = height;
    }
    public String toString(){
        return this.time + " " + this.height;
    }
}
반응형
반응형

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

 

15686번: 치킨 배달

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

www.acmicpc.net

 

 

구현 문제를 풀다 보면 삼성 기출을 자주 만난다.

해당 문제도 삼성 sw 역량테스트 기출문제다.

 

2차원이 나오면 bfs dfs를 의심하게 된다. 하지만 문제를 읽어보면

거리 계산은 단순 수식으로 할 수 있고

백트래킹 문제인 것을 알 수 있다.

 

주어진 M이 치킨집의 개수이니 

치킨집을 조합으로 선택해서 치킨 거리 MIN을 구해주면 끝이다.

 

처음 문제를 풀 때 M을 못 보고

1~N까지 조합 백 트래킹 하며 왜 안될까 생각했다.

 

문제를 잘 읽자

 


 

항상 주의할 점 문제를 잘 읽자

제공해주는 변수(입력)가 무엇인지 보자.

 


코드

 

import java.io.*;
import java.math.BigInteger; // subtract , multiply, add, mod
import java.util.*;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    static StringTokenizer st;
    static StringBuilder sb = new StringBuilder();

    static int[][] arr;
    static int[] temp;
    static List<Node> clist;
    static List<Node> hlist;
    static int globalMin = Integer.MAX_VALUE;

    public static void main(String[] args) throws IOException {

        // 입력부
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");
        int size = Integer.parseInt(st.nextToken());
        int chicken = Integer.parseInt(st.nextToken());
        arr = new int[size][size];

        clist = new ArrayList<>();
        hlist = new ArrayList<>();
        for(int i = 0; i<size; i++){
            st = new StringTokenizer(br.readLine(), " ");
            for(int j = 0; j<size; j++){
                int cHorHo = Integer.parseInt(st.nextToken());
                arr[i][j] = cHorHo;

                if(cHorHo == 2){ // 치킨집
                    clist.add(new Node(i+1, j+1));
                }else if( cHorHo ==1)
                    hlist.add(new Node(i+1, j+1));
            }
        }
        temp = new int[chicken];
        bt(chicken, 0, 0);
        System.out.println(globalMin);
        
    }public static void bt(int limit, int n , int idx){
        if(limit == n){
            int minSum = 0;
            int[] mins = new int[hlist.size()]; // 모든 집 min 저장
            Arrays.fill(mins, 10000);
            for(int i = 0 ; i<hlist.size(); i++){ // 집
                Node hnode = hlist.get(i);
                for(int j = 0; j<limit; j++){ // 치킨
                    Node cnode = clist.get(temp[j]);

                    int n1 = Math.abs(hnode.x - cnode.x) + Math.abs(hnode.y - cnode.y);

                    mins[i] = Math.min(mins[i], n1);
                }
            }
            for(int e : mins)
                minSum+=e;

            globalMin = Math.min(globalMin, minSum);
            return;
        }

        for(int i = idx ; i<clist.size(); i++){
            temp[n] = i;
            bt(limit, n + 1, i + 1);
        }
    }
}
class Node{
    int x;
    int y;
    public Node(int x, int y){
        this.x = x;
        this.y = y;
    }
    public String toString(){
        return this.x+" " + this.y;
    }
}

 

 

반응형
반응형

 

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

 

1326번: 폴짝폴짝

첫째 줄에 징검다리의 개수 N(1≤N≤10,000)이 주어지고, 이어서 각 징검다리에 쓰여 있는 N개의 정수가 주어진다. 그 다음 줄에는 N보다 작거나 같은 자연수 a, b가 주어지는 데, 이는 개구리가 a번

www.acmicpc.net

 

 

 

1차원 BFS 활용 문제다.

 

주의할 점은 도 확인해줘야 한다는 점, 방문하면 최소이므로 방문한 곳을 방문처리해줘야 한다는 점이다.

방문처리를 하지않고 풀었다가 메모리 초과가 생겼다.

 

idx를 이용해서 해당 위치의 배수 값을 찾고배수 값으로 방문하지 않았다면 방문처리해주고 큐에 넣어주면 풀 수 있다.

 

앞과 뒤를 둘다 확인하는 점에 주의하자.

 

 


코드

 

 

import java.io.*;
import java.math.BigInteger; // subtract , multiply, add, mod
import java.util.*;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    static StringTokenizer st;
    static StringBuilder sb = new StringBuilder();

    static int[] arr;
    static int[] dp;
    static boolean[] v;

    public static void main(String[] args) throws IOException {

        int count = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine(), " ");
        arr = new int[count];
        for(int i = 0 ; i<arr.length; i++)
            arr[i] = Integer.parseInt(st.nextToken());
        dp = new int[10001];
        v = new boolean[10001];
        Arrays.fill(dp, 100_000);

        st = new StringTokenizer(br.readLine()," ");
        int s = Integer.parseInt(st.nextToken());
        int e = Integer.parseInt(st.nextToken());

        bfs(s - 1, e - 1, count);
        if(dp[e-1] == 100_000)
            System.out.println(-1);
        else
            System.out.println(dp[e-1]);

    }
    public static void bfs(int s, int e, int count){
        Queue<Node> q = new LinkedList<>();
        q.offer(new Node(s, 1));
        v[s] = true;

        while(!q.isEmpty()){
            Node numbers = q.poll();
            int mul = arr[numbers.idx]; // 배수
            int front = numbers.idx; // <-
            int back = numbers.idx; // ->


            // 뒤를 확인한다.
            while(true){
                back += mul;
                if(back >= count)
                    break;

                if(back == e) { // 찾았다.
                    dp[e] = numbers.jump;
                    return;
                }

                if(!v[back]) {
                    v[back] = true;
                    q.offer(new Node(back, numbers.jump + 1));
                }
            }


            // 앞을 확인한다.
            while(true){
                front -= mul;
                if(front < 0)
                    break;

                if(front == e) { // 찾았다.
                    dp[e] = numbers.jump;
                    return;
                }

                if(!v[front]) {
                    v[front] =true;
                    q.offer(new Node(front, numbers.jump + 1));
                }
            }
        }


    }
}
class Node{
    int idx;
    int jump;
    public Node(int idx, int jump){
        this.idx = idx;
        this.jump = jump;
    }
}
반응형
반응형

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

 

1699번: 제곱수의 합

어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다. 예를 들어 11=32+12+12(3개 항)이다. 이런 표현방법은 여러 가지가 될 수 있는데, 11의 경우 11=22+22+12+12+12(5개 항)도 가능하다

www.acmicpc.net

 


 

다이내믹 프로그래밍을 이용한 문제다.

1, 4, 9, 16.. 과 같은 제곱수를 활용하여 주어진 수 N을 최소한의 개수로 구성하는 것이 답이다.

 

최적의 값을 idx에 기억해두는 바텀업 방식으로 이중 for문을 사용했다.

해당 문제에서 최적의 값은 최소한의 연산 횟수다.

 


 

DP 테이블 초기화

먼저 dp 테이블을 idx는 숫자, value는 최소 연산 횟수로 사용된다.

각 제곱수 idx 는 1로 만들어 주고,

나머지는 min을 알아내야 하니 충분히 큰 값으로 채워놓는다.

 


 

첫 for문은 1부터 100_000까지 차례대로 최소 연산을 구하게 된다.

 

두번째 for문은 최소 연산을 구하기 위해 

j의 제곱이 i 기준 수 보다 크면 의미가 없으므로 continue 하고,

 

j의 제곱이 i 기준 수보다 작다면 몫과 나머지를 구해서 

나머지 값에 해당되는 idx에 해당되는 값 + 현재 몫으로 이전에 구해둔 값과 비교해서 최적의 값을 구할 수 있다.

 

바텀업 방식이니

나머지값에 해당되는 idx는 현재 i 위치보다 이전 값일 것이고, 

최적의 값이라는 것이 보장되고 사용하기 무리가 없다.

 

예전에 풀었던 1463번 1로 만들기 문제와 비슷하다.


코드

 

 

import java.io.*;
import java.math.BigInteger; // subtract , multiply, add, mod
import java.util.*;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    static StringTokenizer st;
    static StringBuilder sb = new StringBuilder();

    public static void main(String[] args) throws IOException {
        int number = Integer.parseInt(br.readLine());

        // dp 초기화
        int[] dp = new int[100001];
        Arrays.fill(dp, 1000_000);
        dp[0] = 0;
        for(int i = 1 ; i * i<dp.length; i++){
            int powNumber = i * i;
            dp[powNumber] = 1;
        }

        // 1부터 100_000 까지 min을 채워나가기
        for(int i = 1 ; i<dp.length; i++){
            for(int j = 325; j >= 1; j--){
                int jNumber = (int)Math.pow(j, 2);

                if(jNumber > i) // 기준숫자보다 제곱이 커지면 의미가 없으니 넘김
                    continue;

                int divide = i / jNumber;
                int rest = i % jNumber;

                dp[i] = Math.min(dp[i], dp[rest] + divide);
            }
        }
        System.out.println(dp[number]);
    }
}

 

 

반응형
반응형

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

 

2206번: 벽 부수고 이동하기

N×M의 행렬로 표현되는 맵이 있다. 맵에서 0은 이동할 수 있는 곳을 나타내고, 1은 이동할 수 없는 벽이 있는 곳을 나타낸다. 당신은 (1, 1)에서 (N, M)의 위치까지 이동하려 하는데, 이때 최단 경로

www.acmicpc.net

 

 


 

처음 시도 때 시간 초과로 풀지 못한 문제다.

 

모든 벽을 큐에 넣고 해당 부분을 0으로 만들었다가 bfs가 종료되면 다시 1로 만드는 식으로

과정을 진행시켰는데 시간초과가 걸렸다.

 

 

생각을 해보면 BFS는 최단 경로를 찾을 수 있고,

Node class를 만들어서 BFS를 하면 해당 Node가 가장 먼저 도착한 Node일 테니 

벽을 뚫은적이 있는지, 없는지에 대한 상태를 저장시키면서 진행하면 될 것 같았다.

 

 

3차원 배열을 이용한 풀이가 있는데, 

3차원을 쓰지 않고, 방문처리 배열을 따로 사용하지 않고도

단순 2차원 graph로 로직 처리를 해주면 풀 수 있다.

 

 

  2
벽을 뚫은 적 x O O O X
벽을 뚫은 적 o O X X X

 

 

BFS 큐에 넣기 전 해당 위치의 값을 확인해서 사용가능성을 판단해주면 된다.

 

 

0

[ 방문하지 않은 상태 ]

 두 상태 다 사용 가능

해당 위치를 재방문 방지를 위해 2로 바꾼다.

 

 

1

[ 벽 ]

방문한 Node가 이미 벽을 뚫었다면 더 뚫을 수 없으므로 continue (더이상 뚫을 수 없음)

방문한 Node가 벽을 안뚫었다면 상태를 벽을 뚫은 상태로 바꾼다. 해당 위치는 다시 사용할 일이 없으므로 값을 3으로 바꾼다.

 

 

2

[ 벽을 뚫은 적 없는 Node를 위해 중간 방문 처리함 ]

방문한 Node가 벽을 뚫은 상태라면  continue (앞서 간 Node가 있음)

방문한 Node가 벽을 뚫지 않은 상태라면 해당 위치를 다신 사용하지 않도록 3으로 바꾼다.

 

 

3

[ 완전 방문된 상태 ]

해당 위치는 모든 노드가 사용할 수 없다.

(벽을 뚫지 않은 Node가 이미 지나간 상태)

 

 

 

 

벽을 뚫은 적이 없는 Node가 해당 위치를 완전 방문 처리하는 우선권이 있다고 생각하고, 최종 결정권을 준다.

방문한 Node의 상태를 확인하여 위치를 4가지 분기로 나눠서 해결해줄 수 있다.

 

 


 

import java.io.*;
import java.util.*;

public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    static StringTokenizer st;
    static StringBuilder sb = new StringBuilder();

    static int[][] graph;

    static Queue<Node> q = new LinkedList<>();
    static int low, col;
    static int[] xRange = {1, -1, 0, 0};
    static int[] yRange = {0, 0, 1, -1};

    public static void main(String[] args) throws IOException {
        StringTokenizer st = new StringTokenizer(br.readLine());

        low = Integer.parseInt(st.nextToken());
        col = Integer.parseInt(st.nextToken());

        graph = new int[low][col];

        for(int i = 0; i< graph.length; i++){
            String str = br.readLine();
            int idx = 0;
            for(int j =0; j< graph[i].length; j++){
                int ele = Integer.parseInt(str.charAt(idx) +"");
                graph[i][j] = ele;
                idx++;
            }
        }
        System.out.println(bfs());
    }

    //
    public static int bfs(){
        int dis = 1;
        q.add(new Node(0, 0, false, dis));
        graph[0][0] = 2;

        int[] xRange = {1, -1, 0, 0};
        int[] yRange = {0, 0, 1, -1};

        while(!q.isEmpty()){
            Node node = q.poll();
            dis = node.dis;
            if(node.x == graph.length - 1 && node.y == graph[0].length -1)
                return dis;

            for(int i = 0; i<4; i++){
                int x = xRange[i] + node.x;
                int y = yRange[i] + node.y;
                if(x>=0 && x<low && y>=0 && y<col){
                    int disn = node.dis + 1;

                    if(graph[x][y] == 3)
                        continue;

                    if(node.state){
                        if(graph[x][y] == 0) {
                            q.offer(new Node(x, y, node.state, disn));
                            graph[x][y] = 2;
                        }

                    }else{
                        if(graph[x][y] == 0 || graph[x][y] == 2){
                            q.offer(new Node(x, y, false, disn));
                            graph[x][y] = 3;
                        }

                        else if(graph[x][y] == 1){ // 벽을 뚫는다.
                            q.offer(new Node(x, y, true, disn));
                            graph[x][y] = 3;
                        }
                    }
                }
            }
        }
        return -1;
    }
}

class Node{
    int x;
    int y;
    boolean state;
    int dis;
    Node(int x, int y, boolean state, int dis){
        this.x = x;
        this.y = y;
        this.state = state;
        this.dis = dis;
    }
}
반응형
반응형

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

 

14501번: 퇴사

첫째 줄에 백준이가 얻을 수 있는 최대 이익을 출력한다.

www.acmicpc.net


 

문제를 풀고 나서 다른 사람들의 풀이를 보니 뒤에서부터 생각해서 푸는 경우도 있던데

막상 시험장이라면 생각해내지 못할 가능성이 많아 보인다.

하던데로 앞에서 푸는 방식으로 해결하는 게 연습하기는 좋을 것 같다.

 

여태까지 풀어왔던 DP방식은 INDEX를 현재 시점으로 두고 이전에 구해논 DP 값으로 비교해서 구하는 경우가 많은데

이 문제는 특이하게 구한 수익을 다음 INDEX+N 방식으로 넘겨주면서 풀어야 해서 생각하기 쉽지 않았던 것 같다.

 

그리고 주의할 점은 미래 수익을 정의하기 이전에

현재 시점의 DP 값이 이전 - 1의 DP 값보다 작으면 이전 DP로 갱신해줘야

계속 최대 수익 값이 이어지면서 원하는 날짜의 최대 수익 값을 구할 수 있다.

 

문제 해결 포인트

 

최대 수익을 이어가기

if dp[i] < dp[i-1] {

   dp[i] = dp[i-1] }

 

 


코드 구현

 

import java.io.*;
import java.util.*;
import java.util.function.*;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 
    static int[] dp = new int[10001];
    static int[] days = new int[10001];
    static int[] costs = new int[10001];
 
    public static void main(String[] args) throws IOException {
        int line = Integer.parseInt(br.readLine());
        int D_day = line + 1;
 
        StringTokenizer st;
        int idx = 1;
        while(line-- > 0){
            st = new StringTokenizer(br.readLine());
            int day =Integer.parseInt(st.nextToken());
            int cost = Integer.parseInt(st.nextToken());
            days[idx] = day;
            costs[idx] = cost;
            idx++;
        }
 
        for(int i = 1; i<=1002; i++){
            int day = days[i]; // 현재에서 부터 진행할 날짜
            int cost = costs[i]; // 마감시 얻는 비용
 
            if(dp[i] < dp[i-1])
                dp[i] = dp[i-1];
 
            if(dp[i+day] < costs[i] + dp[i]) { // 비용 + 현재 최대치 를 미래에 입력
                dp[i + day] = costs[i] + dp[i];
            }
        }
        System.out.println(dp[D_day]);
    }
}
cs

 


 

반응형
반응형

 

투 포인터를 이용한 문제다.

투 포인터를 둘다 시작 지점으로 놨더니

생각할게 너무 많아지고, 시간복잡도도 증가했다.

 

투포인터를 시작지점, 끝점으로 양쪽으로 옮겨서 구현했더니 간단하게 풀렸다.

 

구간합이 아니면 투포인터를 시작지점에 두지 말아야 겠다.

 


코드구현

 

import java.io.*;
import java.util.*;
import java.util.function.*;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
   
    public static void main(String[] args) throws IOException {
        int count = Integer.parseInt(br.readLine());
        int[] arr = new int[count];
 
        int idx = 0;
        StringTokenizer st = new StringTokenizer(br.readLine());
        while(st.hasMoreTokens()){
            arr[idx++= Integer.parseInt(st.nextToken());
        }
        Arrays.sort(arr);
        int goodNumber = 0;
 
        
        for(int i = 0; i<arr.length; i++) {
            int findNumber = arr[i];
 
            // 투포인터
            int s = 0// 시작지점
            int e = arr.length - 1// 배열 끝지점
            int sum = 0;
 
 
            while(s < e){
                sum = arr[s] + arr[e];
                if(sum == findNumber){
                    if(i == s)
                        s++;
                    else if(e == i)
                        e--;
                    else{
                        goodNumber++;
                        break;
                    }
                }
 
                if(arr[s] + arr[e] > findNumber) //find 보다 크면 e--
                    e--;
                else if(arr[s] + arr[e] < findNumber) // find보다 작으면 s++
                    s++;
 
 
            }
        }
        System.out.println(goodNumber);
    }
}
 
 
 
cs

 

 


 

반응형
반응형

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

 

13418번: 학교 탐방하기

입력 데이터는 표준 입력을 사용한다. 입력은 1개의 테스트 데이터로 구성된다. 입력의 첫 번째 줄에는 건물의 개수 N(1 ≤ N ≤ 1,000)과 도로의 개수 M(1 ≤ M ≤ N(N-1)/2) 이 주어진다. 입력의 두 번

www.acmicpc.net


 

최소 신장 트리 (MST)를 알아야 풀 수 있는 문제다.

문제는 (최대 신장 트리 - 최소 신장 트리)의 값을 요구한다.

제곱을 해줘야 하긴 하지만 사소하기 때문에

MST를 적용하면 풀 수 있는 문제다.

 

크루스칼, 프림 모두 적용 가능하며 크루스칼을 이용해서 해결했다.

 

우선순위 큐에서 최대 신장 트리, 최소 신장 트리로 정렬할 수 있도록

두 가지 방식으로 정렬하는 큐를 만들어서 

간선들을 넣어준후 크루스칼 알고리즘을 적용했다.

 

이 문제의 최대 주의할 점은

오르막길이 1이라고 착각할 수 있다.

오르막길은 0이다.

예제는 오르막길이 1이라고 판단해도 정답이 되기 때문에 주의해야 한다.

 


코드구현

 

import java.io.*;
import java.util.*;
 
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 
    static Queue<Edge> q1;
    static Queue<Edge> q2;
    static int[] arr;
 
    public static void main(String[] args) throws IOException {
        StringTokenizer st = new StringTokenizer(br.readLine());
        int E = Integer.parseInt(st.nextToken());
        int V = Integer.parseInt(st.nextToken()) + 1;
 
        arr = new int[E+1];
        for(int i = 0; i<arr.length; i++){
            arr[i] = i;
        }
        q1 = new PriorityQueue<>((o1, o2) -> o1.v - o2.v);
        q2 = new PriorityQueue<>((o1, o2) -> o2.v - o1.v);
 
        while(V-- > 0){
            st = new StringTokenizer(br.readLine());
            int w1 = Integer.parseInt(st.nextToken());
            int w2 = Integer.parseInt(st.nextToken());
            int v = Integer.parseInt(st.nextToken());
 
            Edge e = new Edge(w1,w2,v);
            q1.offer(e);
            q2.offer(e);
        }
 
        //
 
        int ms = (int) Math.pow(mst(0,q1),2); // max
 
        //
 
        for(int i = 0; i<arr.length; i++)
            arr[i] = i;
        int Ms = (int) Math.pow(mst(0,q2), 2); // min
        System.out.println(ms-Ms);
 
    }
    public static int find(int e){ // 대표원소 갖고오기.
        if(arr[e] == e)
            return e;
        else
            return arr[e] = find(arr[e]);
    }
 
    public static void union(int w1, int w2){ // 대표원소 바꿔주기.
        w1 = find(w1);
        w2 = find(w2); // 대표원소를 찾아왔음
        if(w1 < w2){
            arr[w2] = w1;
        }else
            arr[w1] = w2;
    }
 
    public static int mst(int start, Queue<Edge> q){
        int sum = 0;
        while(!q.isEmpty()){
            Edge e = q.poll();
            int w11 = e.w1;
            int w21 = e.w2;
            int v1 = e.v;
 
            if(find(w11) != find(w21)){
                union(w11, w21);
                // 처리부분.
                if(v1 == 0)
                    sum += 1;
            }
        }
        return sum;
    }
}
 
class Edge {
    int w1;
    int w2;
    int v;
    Edge(int w1, int w2, int v){
        this.w1 = w1;
        this.w2 = w2;
        this.v = v;
    }
}
 
 
 
 
cs

 

 

 

 

 


 

반응형
반응형

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

 

2252번: 줄 세우기

첫째 줄에 N(1 ≤ N ≤ 32,000), M(1 ≤ M ≤ 100,000)이 주어진다. M은 키를 비교한 회수이다. 다음 M개의 줄에는 키를 비교한 두 학생의 번호 A, B가 주어진다. 이는 학생 A가 학생 B의 앞에 서야 한다는 의

www.acmicpc.net

 


 

위상정렬을 이용한 문제다.

정점 1의 간선이 정점 3에 방향그래프로 연결되어있다면 1을 처리해준 후 3을 정렬시켜 주면된다.

만약 정점 3을 향해 연결된 정점 2가 하나 더 있다면 정점 3은 정점 2 이후에 나열될 수 있다.

 

정점1과 정점2는 순서가 없으므로

 

1 -> 2 -> 3

2 -> 1 -> 3

 

둘다 가능하므로 나열하는 순서는 뒤바뀔 수 있다.

 


"자신에게 들어오는 간선이 없는 정점"을 확인하기 위해 int 배열을 만들어 준다.
ex) 0, 1, 2, 0, 0 // b에 들어오는 간선 1개, c에 들어오는 간선 2개

처음에 0인 정점을 q에 넣고, 큐를 순회하며 간선이 있다면 향하는쪽 정점을 1씩 줄여주면 된다.
정점이 0이 되면 큐에 넣는다. // 0이 될때는 딱 한번이다. inDegree 를 이미 세주었기 때문에.

코드 구현

 

import java.io.*;
import java.util.*;
 
public class TopologySort {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 
    static List<List<Integer>> list = new ArrayList<>();
    static Queue<Integer> q = new LinkedList<>();
    static int[] isZero;
 
    public static void main(String[] args) throws IOException {
        List<Integer> order = new ArrayList<>();
        StringTokenizer st = new StringTokenizer(br.readLine());
 
        int student = Integer.parseInt(st.nextToken());
        int lines = Integer.parseInt(st.nextToken());
        isZero = new int[student+1];
 
        // 0번은 사용하지 않음.
        for(int i = 0; i<=student; i++)
            list.add(new ArrayList<>());
 
        // 그래프 
        while(lines-- > 0){
            st = new StringTokenizer(br.readLine());
            int first = Integer.parseInt(st.nextToken());
            int second = Integer.parseInt(st.nextToken());
            ++isZero[second];
            list.get(first).add(second);
        }
 
        // 위상정렬
        for (int i = 1; i < isZero.length; i++) { // 모든 edge 확인
            if (isZero[i] == 0)
                q.add(i);
        }
 
        while (!q.isEmpty()) { // 현재 0인 edge
            int edge = q.poll();
            order.add(edge);
            List<Integer> v = list.get(edge);
            for (int e : v) { 
                isZero[e]--;
                if (isZero[e] == 0) { // 0이 됐을때 q에 넣는다.
                    q.add(e);
                }
            }
        }
        order.forEach((n) -> System.out.print(n + " "));
    }
}
 
cs

 

 

 


 

 

 

 

 

반응형
반응형

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

 

1992번: 쿼드트리

첫째 줄에는 영상의 크기를 나타내는 숫자 N 이 주어진다. N 은 언제나 2의 제곱수로 주어지며, 1 ≤ N ≤ 64의 범위를 가진다. 두 번째 줄부터는 길이 N의 문자열이 N개 들어온다. 각 문자열은 0 또

www.acmicpc.net

 

  • 문제 이해
  • 해결 방법
  • 코드 구현

 


문제 이해

 

재귀 함수를 이용한 문제들을 많이 풀어보고 있다. 이전에 풀었던 재귀 문제와 해결 사고 과정이 비슷하여 오래 걸리지 않은 것 같다.

이러한 문제들은 분할적으로 재귀 함수를 호출할 때 index를 조정하는 부분만 잘 처리해주면 금방 풀린다.

 

해당 문제가 다른 문제와 달랐던 점은 출력 방법이 달랐다. 일반적으로 별을 그리거나 개수를 세는 문제인데

괄호와 숫자가 조합되어 출력되어 처음엔 이해가 잘 안 갔다. 자세히 보니 검사 부분에 해당 사각형 부분이 0과 1이

같이 있다면 재귀 함수 호출 이전에 "("를 추가해주고 모든 재귀 함수가 종료되어 제어가 돌아올 때 ")"를 추가해주면 되는 것 같았고, 그 방법이 맞았다.

 

 


해결 방법

 

재귀 함수의 선언부는 (size, 행, 열) 순서로 만들어서 함수를 작성했다.

처음 재귀 함수를 호출할 때 입력 size 크기와 행 index, 열 index를 보내주면서 size를 간격으로 사용했다.

 

그리고 재귀 함수를 호출하기 이전에 먼저 0 또는 1의 총개수가 size * size의 개수만큼 있는지 확인했다.

만약 개수가 맞다면 이 함수는 0 또는 1로 가득 찬 사각형이 맞으므로 더 이상 재귀 함수를 호출하지 않고

StringBuilder에 해당 숫자를 추가해주며 return으로 종료해주었다.

 

만약 다른 경우로 0과 1의 개수가 size * size의 개수와 맞지 않다면 한 사각 형안에서 0과 1이 같이 있다는 의미이므로

4 부분으로 나누어 재귀 함수를 다시 호출해주어 한다. 재귀 함수를 호출하기 이전에 "("를 StringBuilder에 넣어주고

해당 함수에서 호출한 재귀 함수들이 모두 종료될 때 다시 ")"를 추가해주어야 하므로 for문이 종료되면 추가해주도록 했다.

 

for문 안에서는 재귀 함수를 4 부분으로 호출해주기 위해 시작 위치 index를 시작으로 하고 간격만큼 시작 위치에 더해주며

간격을 2번 곱한 지점까지 닿지는 않도록 반복문 종료 조건을 추가해주어 1행 1열, 1행 2열, 2행 1열, 2행 2열 순서로

재귀 함수를 호출해주도록 했다.

 

모든 재귀 함수가 종료되면 main함수에서 StringBuilder를 출력시켜 정답과 일치하는지 보았다.

 

추가적으로 생각했던 건

0과 1의 개수를 셀 때 boolean type으로 false로 지정해두고, 0이 있다면 true로 만드는 부분과 1이 있다면 true로 만드는 부분 두 개를 구현해놓는다면

0과 1이 모두 true일 때 한 사각 형안에 0과 1이 둘 다 있다는 의미이므로 모든 개수를 세지 않아도 되니 시간 복잡도를 조금 줄일순 있을 것 같았다.

그리고 둘다 true가 아니면 0 또는 1만 있다는 뜻이므로 true인 부분만 StringBuilder에 넣어주면 될 것 같은데,

제출하니 시간 초과가 걸리지 않아서 이 부분은 구현하지 않았다.

시간 초과가 걸리면 재귀 함수의 호출의 개수를 줄일순 없으니 이 부분을 수정했을 것 같다.

 

 


코드 구현

 

import java.io.*;
import java.util.*;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static int N;
    static int[][] arr2;
    static StringBuilder sb = new StringBuilder();
 
    public static void main(String[] args) throws IOException {
        // 입력 처리
        StringTokenizer st;
        int N = Integer.parseInt(br.readLine()); 
        arr2 = new int[N][N];
 
        for(int i =0; i<N; i++){
            String str = br.readLine();
            for(int j = 0; j<N; j++){
                arr2[i][j] = str.charAt(j) - '0';
            }
        }
        // 재귀 함수 호출
        recur(N, 00);
        System.out.println(sb);
    }
 
    public static void recur(int size, int low, int col){
        // 0과 1의 개수를 셈
        int oneCount = 0;
        int zeroCount = 0;
        for(int i = low; i < low + size; i++){
            for(int j = col; j < col + size; j++){
                if(arr2[i][j] == 0){
                    zeroCount++;
                }else
                    oneCount++;
            }
        }
 
        // size * size 개수와 0이나 1이 맞다면 StringBuilder에 해당 수를 추가하고 return
        if(zeroCount == size * size || oneCount == size * size){
            if(zeroCount == size * size) {
                sb.append("0");
                return;
            }else{
                sb.append("1");
                return;
            }
        }
 
        // 재귀함수 시작시 "(" 추가
        sb.append("(");


        size /=2;
        
        // 11, 12, 21, 22 순서로 재귀함수 호출
        for(int low1 = low; low1 < low + size * 2; low1 += size){
            for(int col1 = col; col1 < col + size * 2; col1 += size){
                recur(size, low1, col1);
 
            }
        }
        // 모든 재귀함수 종료시 ")"추가
        sb.append(")");
 
    }
}
 
cs
 
 

 

출력 화면

 

반응형

+ Recent posts