반응형

* 핵심

BinaryTree Class를 만들어서 left, right에 자식 이진 트리를 만들어 붙여 트리를 만든다.

* 문제

문제

이진 검색 트리는 다음과 같은 세 가지 조건을 만족하는 이진 트리이다.

  • 노드의 왼쪽 서브트리에 있는 모든 노드의 키는 노드의 키보다 작다.
  • 노드의 오른쪽 서브트리에 있는 모든 노드의 키는 노드의 키보다 크다.
  • 왼쪽, 오른쪽 서브트리도 이진 검색 트리이다.

전위 순회 (루트-왼쪽-오른쪽)은 루트를 방문하고, 왼쪽 서브트리, 오른쪽 서브 트리를 순서대로 방문하면서 노드의 키를 출력한다. 후위 순회 (왼쪽-오른쪽-루트)는 왼쪽 서브트리, 오른쪽 서브트리, 루트 노드 순서대로 키를 출력한다. 예를 들어, 위의 이진 검색 트리의 전위 순회 결과는 50 30 24 5 28 45 98 52 60 이고, 후위 순회 결과는 5 28 24 45 30 60 52 98 50 이다.

이진 검색 트리를 전위 순회한 결과가 주어졌을 때, 이 트리를 후위 순회한 결과를 구하는 프로그램을 작성하시오.

입력

트리를 전위 순회한 결과가 주어진다. 노드에 들어있는 키의 값은 106보다 작은 양의 정수이다. 모든 값은 한 줄에 하나씩 주어지며, 노드의 수는 10,000개 이하이다. 같은 키를 가지는 노드는 없다.

출력

입력으로 주어진 이진 검색 트리를 후위 순회한 결과를 한 줄에 하나씩 출력한다.

* 소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    public static StringBuilder sb = new StringBuilder();
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BinaryTree tree = new BinaryTree(Integer.parseInt(br.readLine()));
        String s = "";
        while ((s = br.readLine()) != null && s.length() != 0) {    //EOF까지 입력받음
            tree = tree.addTree(tree, Integer.parseInt(s));
        }
        postorder(tree);
        System.out.println(sb);
    }
 
    public static void postorder(BinaryTree tree) {
        if (tree.left != null) postorder(tree.left);
        if (tree.right != null) postorder(tree.right);
        sb.append(tree.data + "\n");
    }
}
 
class BinaryTree {
    int data;
    BinaryTree left;
    BinaryTree right;
 
    public BinaryTree(int data) {
        this.data = data;
        this.left = null;
        this.right = null;
    }
 
    public BinaryTree addTree(BinaryTree tree, int val) {
        BinaryTree curTree = null;
        if (tree == nullreturn new BinaryTree(val);
        if (tree.data > val) {
            curTree = addTree(tree.left, val);  //새로운 하위 이진 트리 생성
            tree.left = curTree;                //부모 노드의 왼쪽에 붙인다
        } else if (tree.data < val) {
            curTree = addTree(tree.right, val); //새로운 하위 이진 트리 생성
            tree.right = curTree;               //부모 노드의 오른쪽에 붙인다
        }
        return tree;                            //새로 붙인 트리 return
    }
}
 
cs


반응형
반응형

* 문제 핵심

세그먼트 트리를 이용하여 sum-tree 구현 및 값 변경

* 문제

문제

모두 알다시피 동호는 커피숍의 마담이다. (마담이 무엇인지는 본인에게 물어보도록 하자.)

어느날 커피숍의 손님 A씨가 동호에게 게임을 하자고 했다.

그 게임은 다음과 같은 규칙을 갖는다.

N개의 정수가 있으면, 동호는 다음과 같이 말한다. “3~7번째 수의 합은 무엇이죠?” 그러면 상대방은 “그 답은 000입니다. 그리고 8번째 수를 2로 고치도록 하죠” 그러면 동호는 “네 알겠습니다.”라고 한 뒤에 다시 상대방이 동호가 했던 것처럼 “8~9번째 수의 합은 무엇이죠?”라고 묻게된다. 이 것을 번갈아 가면서 반복하는 게임이다.

당신은 이 게임의 심판 역을 맡았다. 요컨대, 질문에 대한 답들을 미리 알아야 한다는 것이다.

당신의 머리가 출중하다면 10만개 가량 되는 정수와 10만턴 정도 되는 게임을 기억할 수 있을 것이다. 몇판 이 게임을 즐기던 동호는 많은 사람들이 이 게임을 하기를 바라게 되었고, 당신에게 심판 프로그램을 구현해달라고 요청했다.

입력

첫째 줄에 수의 개수 N과 턴의 개수 Q가 주어진다.(1 ≤ N, Q ≤ 100,000) 둘째 줄에는 처음 배열에 들어가 있는 정수 N개가 주어진다. 세 번째 줄에서 Q+2번째 줄까지는 x y a b의 형식으로 x~y까지의 합을 구하여라, a번째 수를 b로 바꾸어라 라는 뜻의 데이터가 주어진다.

입력되는 모든 수는 32비트 부호있는 정수이다.

출력

한 턴마다 구한 합을 한 줄마다 한 개씩 출력한다.

* 소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    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 q = Integer.parseInt(stk.nextToken());
        stk = new StringTokenizer(br.readLine());
        SegmentTree st = new SegmentTree(n);
        for (int i = 1; i <= n; i++) {
            int v = Integer.parseInt(stk.nextToken());
            st.update(i, v);
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < q; i++) {
            stk = new StringTokenizer(br.readLine());
            int x = Integer.parseInt(stk.nextToken());
            int y = Integer.parseInt(stk.nextToken());
            int max = Math.max(x, y);
            int min = Math.min(x, y);
            x = min;
            y = max;
            int a = Integer.parseInt(stk.nextToken());
            int b = Integer.parseInt(stk.nextToken());
            sb.append(st.getSum(x, y) + "\n");
            st.update(a, b);
        }
        System.out.println(sb);
    }
}
 
class SegmentTree {
    long[] tree;
    int s;
 
    public SegmentTree(int n) {
        for (s = 1; s < n; s *= 2)
            ;
        tree = new long[s * 2];
        for (int i = 1; i < s + s; i++)
            tree[i] = 0l;
    }
 
    void update(int x, long v) {
        int l = x + s - 1;
        tree[l] = v;
        l /= 2;
        while (l >= 1) {
            tree[l] = tree[l * 2+ tree[l * 2 + 1];
            l /= 2;
        }
    }
 
    long getMin(int Left, int Right) {
        int l = Left + s - 1, r = Right + s - 1;
        long rval = Long.MAX_VALUE;
        while (l <= r) {
            if (l % 2 == 0)
                l /= 2;
            else {
                rval = Math.min(rval, tree[l]);
                l = (l / 2+ 1;
            }
            if (r % 2 == 1)
                r /= 2;
            else {
                rval = Math.min(rval, tree[r]);
                r = (r / 2- 1;
            }
        }
        return rval;
    }
 
    long getSum(int Left, int Right) {
        int l = Left + s - 1, r = Right + s - 1;
        long rval = 0;
        while (l <= r) {
            if (l % 2 == 0)
                l /= 2;
            else {
                rval += tree[l];
                l = (l / 2+ 1;
            }
            if (r % 2 == 1)
                r /= 2;
            else {
                rval += tree[r];
                r = (r / 2- 1;
            }
        }
        return rval;
    }
}
cs


반응형
반응형

* 문제 핵심

세그먼트 트리를 이용하여 sum-tree 구현 및 값 변경

* 문제

문제

어떤 N개의 수가 주어져 있다. 그런데 중간에 수의 변경이 빈번히 일어나고 그 중간에 어떤 부분의 합을 구하려 한다. 만약에 1,2,3,4,5 라는 수가 있고, 3번째 수를 6으로 바꾸고 2번째부터 5번째까지 합을 구하라고 한다면 17을 출력하면 되는 것이다. 그리고 그 상태에서 다섯 번째 수를 2로 바꾸고 3번째부터 5번째까지 합을 구하라고 한다면 12가 될 것이다.

입력

첫째 줄에 수의 개수 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번째 수까지의 합을 구하여 출력하면 된다.

a가 1인 경우 c는 long long 범위를 넘지 않는다.

출력

첫째 줄부터 K줄에 걸쳐 구한 구간의 합을 출력한다. 단, 정답은 long long 범위를 넘지 않는다.

*  소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    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());
        int k = Integer.parseInt(stk.nextToken());
        SegmentTree st = new SegmentTree(n);
        for (int i = 1; i <= n; i++) {
            long v = Long.parseLong(br.readLine());
            st.update(i, v);
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < m + k; i++) {
            stk = new StringTokenizer(br.readLine());
            int type = Integer.parseInt(stk.nextToken());
            if (type == 1) {
                st.update(Integer.parseInt(stk.nextToken()), Long.parseLong(stk.nextToken()));
            } else {
                sb.append(st.getSum(Integer.parseInt(stk.nextToken()), Integer.parseInt(stk.nextToken()))).append('\n');
            }
        }
        System.out.print(sb);
    }
 
}
 
class SegmentTree {
    long[] tree;
    int s;
 
    public SegmentTree(int n) {
        for (s = 1; s < n; s *= 2)
            ;
        tree = new long[s * 2];
        for (int i = 1; i < s + s; i++)
            tree[i] = 0l;
    }
 
    void update(int x, long v) {
        int l = x + s - 1;
        tree[l] = v;
        l /= 2;
        while (l >= 1) {
            tree[l] = tree[l * 2+ tree[l * 2 + 1];
            l /= 2;
        }
    }
 
    long getMin(int Left, int Right) {
        int l = Left + s - 1, r = Right + s - 1;
        long rval = Long.MAX_VALUE;
        while (l <= r) {
            if (l % 2 == 0)
                l /= 2;
            else {
                rval = Math.min(rval, tree[l]);
                l = (l / 2+ 1;
            }
            if (r % 2 == 1)
                r /= 2;
            else {
                rval = Math.min(rval, tree[r]);
                r = (r / 2- 1;
            }
        }
        return rval;
    }
 
    long getSum(int Left, int Right) {
        int l = Left + s - 1, r = Right + s - 1;
        long rval = 0;
        while (l <= r) {
            if (l % 2 == 0)
                l /= 2;
            else {
                rval += tree[l];
                l = (l / 2+ 1;
            }
            if (r % 2 == 1)
                r /= 2;
            else {
                rval += tree[r];
                r = (r / 2- 1;
            }
        }
        return rval;
    }
}
cs


반응형
반응형

* 핵심

우선순위큐를 이용하여 다익스트라를 구현

* 문제

문제

방향그래프가 주어지면 주어진 시작점에서 다른 모든 정점으로의 최단 경로를 구하는 프로그램을 작성하시오. 단, 모든 간선의 가중치는 10 이하의 자연수이다.

입력

첫째 줄에 정점의 개수 V와 간선의 개수 E가 주어진다. (1≤V≤20,000, 1≤E≤300,000) 모든 정점에는 1부터 V까지 번호가 매겨져 있다고 가정한다. 둘째 줄에는 시작 정점의 번호 K(1≤K≤V)가 주어진다. 셋째 줄부터 E개의 줄에 걸쳐 각 간선을 나타내는 세 개의 정수 (u, v, w)가 순서대로 주어진다. 이는 u에서 v로 가는 가중치 w인 간선이 존재한다는 뜻이다. u와 v는 서로 다르며 w는 10 이하의 자연수이다. 서로 다른 두 정점 사이에 여러 개의 간선이 존재할 수도 있음에 유의한다.

출력

첫째 줄부터 V개의 줄에 걸쳐, i번째 줄에 i번 정점으로의 최단 경로의 경로값을 출력한다. 시작점 자신은 0으로 출력하고, 경로가 존재하지 않는 경우에는 INF를 출력하면 된다.

* 소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer stk = new StringTokenizer(br.readLine());
        int V = Integer.parseInt(stk.nextToken());
        int E = Integer.parseInt(stk.nextToken());
        int start = Integer.parseInt(br.readLine());
 
        ArrayList<Edge>[] edge = new ArrayList[V + 1];
        for (int i = 1; i <= V; i++) edge[i] = new ArrayList<>();
        int[] dijkstra = new int[V + 1];
        Arrays.fill(dijkstra, -1);
 
        for (int i = 0; i < E; i++) {
            stk = new StringTokenizer(br.readLine());
            int u = Integer.parseInt(stk.nextToken());
            int v = Integer.parseInt(stk.nextToken());
            int w = Integer.parseInt(stk.nextToken());
            edge[u].add(new Edge(u, v, w));
        }
        PriorityQueue<Edge> pq = new PriorityQueue<>();
        boolean[] visited = new boolean[V + 1];
        pq.add(new Edge(0, start, 0));
        while (!pq.isEmpty()) {
            Edge e = pq.poll();
            if (!visited[e.v]) {
                visited[e.v] = true;
                dijkstra[e.v] = e.w;
                for (int i = 0; i < edge[e.v].size(); i++) {
                    pq.add(new Edge(e.v, edge[e.v].get(i).v, e.w + edge[e.v].get(i).w));    //해당 간선과 연결된 간선 
                }
            }
        }
 
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= V; i++) {
            int tmp = dijkstra[i];
            if (tmp != -1) sb.append(tmp + "\n");
            else sb.append("INF\n");
        }
        System.out.print(sb);
    }
 
 
    public static class Edge implements Comparable<Edge> {
        int u, v, w;
 
        Edge(int u, int v, int w) {
            this.u = u;
            this.v = v;
            this.w = w;
        }
 
        @Override
        public int compareTo(Edge o) {
            return w - o.w;     //오름차순 정렬
        }
    }
}
 
cs


반응형
반응형

* 핵심

MST의 Union-Find를 이용하여 최소비용을 출력

* 문제

문제

도현이는 컴퓨터와 컴퓨터를 모두 연결하는 네트워크를 구축하려 한다. 하지만 아쉽게도 허브가 있지 않아 컴퓨터와 컴퓨터를 직접 연결하여야 한다. 그런데 모두가 자료를 공유하기 위해서는 모든 컴퓨터가 연결이 되어 있어야 한다. (a와 b가 연결이 되어 있다는 말은 a에서 b로의 경로가 존재한다는 것을 의미한다. a에서 b를 연결하는 선이 있고, b와 c를 연결하는 선이 있으면 a와 c는 연결이 되어 있다.)

그런데 이왕이면 컴퓨터를 연결하는 비용을 최소로 하여야 컴퓨터를 연결하는 비용 외에 다른 곳에 돈을 더 쓸 수 있을 것이다. 이제 각 컴퓨터를 연결하는데 필요한 비용이 주어졌을 때 모든 컴퓨터를 연결하는데 필요한 최소비용을 출력하라. 모든 컴퓨터를 연결할 수 없는 경우는 없다.

입력

첫째 줄에 컴퓨터의 수 N (1 ≤ N ≤ 1000)가 주어진다.

둘째 줄에는 연결할 수 있는 선의 수 M (1 ≤ M ≤ 100,000)가 주어진다.

셋째 줄부터 M+2번째 줄까지 총 M개의 줄에 각 컴퓨터를 연결하는데 드는 비용이 주어진다. 이 비용의 정보는 세 개의 정수로 주어지는데, 만약에 a b c 가 주어져 있다고 하면 a컴퓨터와 b컴퓨터를 연결하는데 비용이 c (1 ≤ c ≤ 10,000) 만큼 든다는 것을 의미한다.

출력

모든 컴퓨터를 연결하는데 필요한 최소비용을 첫째 줄에 출력한다.

* 소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    public static int[] disjoint;
 
    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());
 
        Edge[] edge = new Edge[m];
        for (int i = 0; i < m; i++) {
            StringTokenizer stk = new StringTokenizer(br.readLine());
            int u = Integer.parseInt(stk.nextToken());
            int v = Integer.parseInt(stk.nextToken());
            int w = Integer.parseInt(stk.nextToken());
            edge[i] = new Edge(u, v, w);
        }
        Arrays.sort(edge);
        disjoint = new int[n + 1];
        for (int i = 0; i < n + 1; i++) {
            disjoint[i] = i;
        }
        int ans = 0;
        for (int i = 0; i < m; i++) {       //union-find
            Edge e = edge[i];
            int u = e.u;
            int v = e.v;
            int x = find(u);        //find
            int y = find(v);        //find
            if (x != y) {           //union
                disjoint[x] = y;
                ans += e.w;
            }
        }
        System.out.println(ans);
    }
 
    public static int find(int x) {
        if (disjoint[x] == x) return x;
        else return disjoint[x] = find(disjoint[x]);
    }
 
    public static class Edge implements Comparable<Edge> {
        int u, v, w;
 
        public Edge(int u, int v, int w) {
            this.u = u;
            this.v = v;
            this.w = w;
        }
 
        @Override
        public int compareTo(Edge o) {
            return this.w - o.w;
        }
    }
}
cs


반응형
반응형

* 핵심

2중 for문을 돌리면서 증가수열이면 max값을 선택해가는 방식

* 문제

문제

수열 A가 주어졌을 때, 그 수열의 증가 부분 수열 중에서 합이 가장 큰 것을 구하는 프로그램을 작성하시오.

예를 들어, 수열 A = {1, 100, 2, 50, 60, 3, 5, 6, 7, 8} 인 경우에 합이 가장 큰 증가 부분 수열은 A = {1, 100, 25060, 3, 5, 6, 7, 8} 이고, 합은 113이다.

입력

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000)이 주어진다.

둘째 줄에는 수열 A를 이루고 있는 Ai가 주어진다. (1 ≤ Ai ≤ 1,000)

출력

첫째 줄에 수열 A의 합이 가장 큰 증가 부분 수열의 합을 출력한다.

* 소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        StringTokenizer stk = new StringTokenizer(br.readLine());
        int[] num = new int[n + 1];
        int[] dp = new int[n + 1];
 
        for (int i = 1; i <= n; i++) {
            num[i] = Integer.parseInt(stk.nextToken());
        }
 
        dp[1= num[1];
        int max = dp[1];
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                if (num[i] > num[j]) {      // i,j 가 증가수열이면 max값 선택
                    dp[i] = Math.max(dp[i], dp[j]);
                }
            }
            dp[i] += num[i];
            max = Math.max(max, dp[i]);
        }
        System.out.println(max);
    }
}
 
cs


반응형
반응형

* 핵심

2가지의 경우의 수를 생각해서 Top-Down 방식 사용

1. 왼쪽이 오른쪽보다 높은 경우     => 오른쪽 depth+1, dp[left][right]에 오른쪽 카드 값 추가

2. 왼쪽이 오른쪽보다 낮은 경우    => 왼쪽 depth+1 과 왼쪽 depth+1, 오른쪽 depth+1 값 중 max값 비교

최종적으로 1번값과 2번값의 max값을 dp[left][right]값으로 선택

* 문제

문제

지훈이는 최근에 혼자 하는 카드게임을 즐겨하고 있다. 게임에 사용하는 각 카드에는 양의 정수 하나가 적혀있고 같은 숫자가 적힌 카드는 여러 장 있을 수 있다. 게임방법은 우선 짝수개의 카드를 무작위로 섞은 뒤 같은 개수의 두 더미로 나누어 하나는 왼쪽에 다른 하나는 오른쪽에 둔다. 그리고 빈 통을 하나 준비한다. 

이제 각 더미의 제일 위에 있는 카드끼리 서로 비교하며 게임을 한다. 게임 규칙은 다음과 같다. 지금부터 왼쪽 더미의 제일 위 카드를 왼쪽 카드로, 오른쪽 더미의 제일 위 카드를 오른쪽 카드로 부르겠다.

  1. 언제든지 왼쪽 카드만 통에 버릴 수도 있고 왼쪽 카드와 오른쪽 카드를 둘 다 통에 버릴 수도 있다. 이때 얻는 점수는 없다.
  2. 오른쪽 카드에 적힌 수가 왼쪽 카드에 적힌 수보다 작은 경우에는 오른쪽 카드만 통에 버릴 수도 있다. 오른쪽 카드만 버리는 경우에는 오른쪽 카드에 적힌 수만큼 점수를 얻는다.
  3. (1)과 (2)의 규칙에 따라 게임을 진행하다가 어느 쪽 더미든 남은 카드가 없다면 게임이 끝나며 그때까지 얻은 점수의 합이 최종 점수가 된다. 

다음 예는 세 장 씩 두 더미의 카드를 가지고 게임을 시작하는 경우이다

카드 순서왼쪽 더미오른쪽 더미
132
224
351

이 경우, 우선 오른쪽 카드 2가 왼쪽 카드 3보다 작으므로 규칙 (1)에 따라 왼쪽 카드만 버리거나 왼쪽 카드와 오른쪽 카드를 모두 버리거나, 규칙 (2)에 따라 오른쪽 카드만 버릴 수 있다. 만약 오른쪽 카드만 버리는 것으로 선택하면, 2만큼 점수를 얻고 오른쪽 카드 2는 버린다. 이제 오른쪽 더미의 제일 위 카드는 4이고 이는 왼쪽 카드 3보다 크므로 규칙 (1)에 따라 왼쪽 카드만 버리거나 왼쪽 카드와 오른쪽 카드를 둘 다 버릴 수 있다. 만약 둘 다 버리는 것으로 선택하면, 이제 왼쪽 카드는 2가 되고 오른쪽 카드는 1이 된다. 이 경우 다시 규칙 (1)과 (2)에 따라 세 가지 중 한가지를 선택할 수 있고, 그 중 왼쪽 카드만 버리는 것으로 선택하면 이제 왼쪽 카드는 5가 되고 오른쪽 카드는 1이 된다. 이 경우에도 역시 규칙 (1)과 (2)에 따라 세 가지 중 한가지를 선택할 수 있고, 그 중 오른쪽 카드만 버리는 것으로 선택하면 1만큼 점수를 얻고 오른쪽 카드 1은 버린다. 이제 오른쪽 더미에는 남은 카드가 없으므로 규칙 (3)에 따라 게임이 끝나며 최종 점수는 2+1=3이 된다.

두 더미의 카드가 주어졌을 때, 게임을 통해 얻을 수 있는 최종 점수의 최댓값을 출력하는 프로그램을 작성하시오. 위 예에서 최종 점수의 최댓값은 7이다.

입력

첫 줄에는 한 더미의 카드의 개수를 나타내는 자연수 N(1 ≤ N ≤ 2,000)이 주어진다. 다음 줄에는 왼쪽 더미의 카드에 적힌 정수 A(1 ≤ A ≤ 2,000)가 카드 순서대로 N개 주어진다. 그 다음 줄에는 오른쪽 더미의 카드에 적힌 정수 B(1 ≤ B ≤ 2,000)가 카드 순서대로 N개 주어진다. 각 더미에는 같은 숫자를 가진 카드가 두 개 이상 있을 수 있다.

출력

얻을 수 있는 최종 점수의 최댓값을 출력한다.

* 소스 코드

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
import java.io.*;
import java.util.*;
 
public class Main {
    public static int n;
    public static int[][] card, dp;
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        card = new int[n][2];
        dp = new int[n][n];     // [leftDepth][rightDepth]
        for (int i = 0; i < n; i++) {
            Arrays.fill(dp[i], -1);
        }
        for (int i = 0; i < 2; i++) {
            StringTokenizer stk = new StringTokenizer(br.readLine());
            for (int j = 0; j < n; j++) {
                card[j][i] = Integer.parseInt(stk.nextToken());
            }
        }
        System.out.println(TopDown(00));
    }
 
    public static int TopDown(int leftDepth, int rightDepth) {
        if (leftDepth == n || rightDepth == n) return 0;                        //종료 조건
        if (dp[leftDepth][rightDepth] != -1return dp[leftDepth][rightDepth];  //메모이제이션
        else {
            dp[leftDepth][rightDepth] = 0;
            if (card[leftDepth][0> card[rightDepth][1])           //왼쪽이 오른쪽보다 큰 경우
                dp[leftDepth][rightDepth] = TopDown(leftDepth, rightDepth + 1+ card[rightDepth][1];
            dp[leftDepth][rightDepth] = Math.max(TopDown(leftDepth + 1, rightDepth + 1),
                    Math.max(TopDown(leftDepth + 1, rightDepth), dp[leftDepth][rightDepth]));
        }
        return dp[leftDepth][rightDepth];
    }
}
cs


반응형
반응형

* 핵심

포도주를 연속으로 3잔을 마실 수 없다.

포도주를 연속으로 2잔을 안마시는 경우를 생각해야 한다. 

Ex) 100, 2, 1, 4, 100의 경우 204가 나와야 한다.


* 문제

문제

효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규칙이 있다.

  1. 포도주 잔을 선택하면 그 잔에 들어있는 포도주는 모두 마셔야 하고, 마신 후에는 원래 위치에 다시 놓아야 한다.
  2. 연속으로 놓여 있는 3잔을 모두 마실 수는 없다.

효주는 될 수 있는 대로 많은 양의 포도주를 맛보기 위해서 어떤 포도주 잔을 선택해야 할지 고민하고 있다. 1부터 n까지의 번호가 붙어 있는 n개의 포도주 잔이 순서대로 테이블 위에 놓여 있고, 각 포도주 잔에 들어있는 포도주의 양이 주어졌을 때, 효주를 도와 가장 많은 양의 포도주를 마실 수 있도록 하는 프로그램을 작성하시오. 

예를 들어 6개의 포도주 잔이 있고, 각각의 잔에 순서대로 6, 10, 13, 9, 8, 1 만큼의 포도주가 들어 있을 때, 첫 번째, 두 번째, 네 번째, 다섯 번째 포도주 잔을 선택하면 총 포도주 양이 33으로 최대로 마실 수 있다.

입력

첫째 줄에 포도주 잔의 개수 n이 주어진다. (1≤n≤10,000) 둘째 줄부터 n+1번째 줄까지 포도주 잔에 들어있는 포도주의 양이 순서대로 주어진다. 포도주의 양은 1,000 이하의 음이 아닌 정수이다.

출력

첫째 줄에 최대로 마실 수 있는 포도주의 양을 출력한다.


* 소스 코드 

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
import java.io.*;
import java.util.*;
 
public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer stk = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(stk.nextToken());
        int[] wine = new int[n + 1];
        int[][] dp = new int[10001][2];     //[x][?] : 이전에 방문한 포도주가 1칸 전에 있는지 or 2칸전에 있는지
 
        for (int i = 0; i < n; i++) {
            wine[i] = Integer.parseInt(br.readLine());
        }
        dp[0][0= wine[0];
        dp[1][0= wine[0+ wine[1];
        dp[1][1= wine[1];
        if (n >= 2) {
            dp[2][0= dp[1][1+ wine[2];
            dp[2][1= dp[0][0+ wine[2];
            for (int i = 3; i <= n; i++) {      //목적지가 마지막이 아니기 때문에 n+1번 계산
                dp[i][0= dp[i - 1][1+ wine[i];
                dp[i][1= Math.max(dp[i - 3][0], Math.max(dp[i - 2][0], dp[i - 2][1])) + wine[i];
            }
        }
        System.out.println(Math.max(Math.max(dp[n][0], dp[n][1]), Math.max(dp[n - 1][0], dp[n - 1][1])));
    }
}
cs


반응형