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

 

1563번: 개근상

백준중학교에서는 학기가 끝날 무렵에 출결사항을 보고 개근상을 줄 것인지 말 것인지 결정한다. 이 학교는 이상해서 학생들이 학교를 너무 자주 빠지기 때문에, 개근상을 주는 조건이 조금 독

www.acmicpc.net

점화식 구하는게 좀 복잡할 수도 있는 dp문제입니다.

문제를 풀고나서 다른분들의 풀이를 보니 3차원배열을 사용하셨던데 저는 2차원 dp배열로 해결하였습니다.

 

우선 이 문제는 지각을 두 번 이상 하거나, 결석을 세 번 연속으로 하는 경우를 제외한 모든 경우의 수를 구해야합니다.

 

손으로 직접 그려본 후에 점화식을 찾았습니다.

dp[N][start] : 학기 첫날(start)이 출석 / 지각 / 결석일 때 N번째 날의 경우의 수

		[0]	[1]	[2]	sum
N = 1	 1	1	1	3

N = 2	 3	2	3	8

N = 3	 8	4	7	19

N = 4	 19	7	17	43

N = 5	 43	13	38	94

N = 6	 94	24	82	200

 

dp[N][0] : 첫날이 출석인 경우의 수

 -> dp[N - 1][0] + dp[N - 1][1] + dp[N - 1][2]

dp[N][1] : 첫날이 지각인 경우의 수

 -> dp[N - 1][1] + dp[N - 2][1] + dp[N - 3][1]

dp[N][2] : 첫날이 결석인 경우의 수

 -> dp[N - 1][0] + dp[N - 1][1] + dp[N - 2][0] + dp[N - 2][1]

 

출력은 dp[N][0 ~ 2]를 더해 1000000으로 mod연산한 값을 출력해주시면 됩니다.

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
#include <iostream>
#define MOD 1000000
using namespace std;
 
int dp[1001][3];
int N;
 
void init() {
    dp[0][1= 1;
    dp[1][0= 1;
    dp[1][1= 1;
    dp[1][2= 1;
    dp[2][0= 3;
    dp[2][1= 2;
    dp[2][2= 3;
    for (int i = 3; i <= 1000; i++) {
        dp[i][0= (dp[i - 1][0+ dp[i - 1][1+ dp[i - 1][2]) % MOD;
        dp[i][1= (dp[i - 1][1+ dp[i - 2][1+ dp[i - 3][1]) % MOD;
        dp[i][2= (dp[i - 1][0+ dp[i - 1][1+ dp[i - 2][0+ dp[i - 2][1]) % MOD;
    }
}
 
void input() {
    cin >> N;
    cout << (dp[N][0+ dp[N][1+ dp[N][2]) % MOD << '\n';
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    init();
    input();
 
    return 0;
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 2186 문자판  (0) 2021.06.19
boj 13325 이진 트리  (0) 2021.06.18
boj 10800 컬러볼  (0) 2021.04.09
boj 12869 뮤탈리스크  (0) 2021.04.04
boj 1520 내리막 길  (0) 2021.03.28

www.acmicpc.net/problem/10800

 

10800번: 컬러볼

첫 줄에는 공의 개수를 나타내는 자연수 N이 주어진다(1 ≤ N ≤ 200,000). 다음 N개의 줄 중 i번째 줄에는 i번째 공의 색을 나타내는 자연수 Ci와 그 크기를 나타내는 자연수 Si가 주어진다(1 ≤ Ci ≤ N

www.acmicpc.net

 

어떤 공은 자신보다 색이 같거나 크기가 크거나 같은 공을 잡을 수 없습니다.

즉, 색이 다르고, 크기가 작은 공을 잡을 수 있습니다.

 

저는 공의 색에 대한 누적합(colorsum), 크기에 대한 누적합(sizesum), 전체 누적합(sum)을 모두 구하면서 답을 찾았습니다.

 

우선 공의 크기 -> 색 별로 오름차순 정렬합니다. (출력할 때 인덱스를 맞춰줘야하니 인덱스를 유지해줍니다.)

이제 크기가 작은 공부터 하나씩 꺼내 sum, colorsum, sizesum을 갱신합니다.

 

그럼 현재 공이 잡을 수 있는 공은

전체 합 - 같은 색의 크기 합 - 같은 크기의 크기 합 + 자신의 크기로 구해주시면 됩니다.

(같은 색과 같은 크기는 잡을 수 없으며 자신은 두 번 뺀 값이기때문에 자신의 크기를 더한 것입니다.)

 

하지만 이걸로 끝내기엔 예외가 하나 더 있습니다.

"자신과 색과 크기 모두 같은 공" 끼리는 답이 모두 같습니다.

저는 여기에서 예외처리가 안되어 pre배열에 이전 공의 정보를 유지하였습니다.

이전 공의 색과 크기가 모두 같으면 이전 ans값을 넣어줍니다.

 

 

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
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.StringTokenizer;
 
public class Main {
    static class Pair {
        int idx;
        int color;
        int size;
 
        public Pair(int idx, int color, int size) {
            this.idx = idx;
            this.color = color;
            this.size = size;
        }
    }
 
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static ArrayList<Pair> list;
    static int sum[] = new int[200001];
    static int colorsum[] = new int[200001];
    static int sizesum[] = new int[2001];
    static int ans[] = new int[200001];
    static int N;
 
    static void func() {
        int pre[] = new int[3];
        for (int i = 1; i <= N; i++) {
            int idx = list.get(i - 1).idx;
            int color = list.get(i - 1).color;
            int size = list.get(i - 1).size;
 
            sum[i] = sum[i - 1+ size;
            colorsum[color] += size;
            sizesum[size] += size;
 
            if (color == pre[1&& size == pre[2])
                ans[idx] = ans[pre[0]];
            else
                ans[idx] = sum[i] - colorsum[color] - sizesum[size] + size;
 
            pre[0= idx;
            pre[1= color;
            pre[2= size;
        }
 
        StringBuffer sb = new StringBuffer();
        for (int i = 1; i <= N; i++) {
            sb.append(ans[i]).append("\n");
        }
        System.out.println(sb.toString());
    }
 
    static void input() throws Exception {
        int x, y;
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        list = new ArrayList<>();
        for (int i = 1; i <= N; i++) {
            st = new StringTokenizer(br.readLine());
            x = Integer.parseInt(st.nextToken());
            y = Integer.parseInt(st.nextToken());
            list.add(new Pair(i, x, y));
        }
 
        Collections.sort(list, new Comparator<Pair>() {
            @Override
            public int compare(Pair o1, Pair o2) {
                if (o1.size == o2.size)
                    return o1.color - o2.color;
                else
                    return o1.size - o2.size;
            }
        });
    }
 
    public static void main(String[] args) throws Exception {
        input();
        func();
    }
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 13325 이진 트리  (0) 2021.06.18
boj 1563 개근상  (0) 2021.06.02
boj 12869 뮤탈리스크  (0) 2021.04.04
boj 1520 내리막 길  (0) 2021.03.28
boj 1937 욕심쟁이 판다  (0) 2021.03.28

www.acmicpc.net/problem/12869

 

12869번: 뮤탈리스크

1, 3, 2 순서대로 공격을 하면, 남은 체력은 (12-9, 10-1, 4-3) = (3, 9, 1)이다. 2, 1, 3 순서대로 공격을 하면, 남은 체력은 (0, 0, 0)이다.

www.acmicpc.net

dp[hp1][hp2][hp3] : SCV의 체력이 각각 hp1, hp2, hp3 남았을 때 공격해야할 횟수의 최솟값

 

뮤탈리스크는 일반적으로 한 번 공격할 때 3기의 SCV를 공격할 수 있습니다.

 

스타크래프트에서는 9, 6, 3 순서대로 데미지가 들어가지만 이 문제에서는 9, 3, 1 순서대로 데미지가 들어갑니다.

(이거를 쿠션데미지라고 합니다.)

 

SCV의 체력이 0이하가 되면 파괴되고, 한 번의 공격에서 같은 SCV를 두 번이상 공격할 수 없습니다.

1번 SCV를 먼저 공격했을 때 쿠션데미지를 2번, 3번 순으로 넣을 때 / 3번, 2번 순으로 넣을 때를 각각 구해줍니다.

마찬가지로 2번, 3번 SCV를 먼저 공격했을 때도 쿠션데미지를 넣는 경우를 각각 구해줍니다.

 

정리하자면

1 -> 2 -> 3

1 -> 3 -> 2

 

2 -> 1 -> 3

2 -> 3 -> 1

 

3 -> 1 -> 2

3 -> 2 -> 1

위의 순서를 모두 구한 것의 최솟값을 출력해주시면 됩니다.

재귀를 돌리는 도중에 체력이 0이하가 되면 0으로 맞춰줘야합니다. (배열의 음수인덱스 참조 가능성)

 

 

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
#include <iostream>
#include <cstring>
#include <algorithm>
#define INF 2147483647
using namespace std;
 
int dp[61][61][61];
int list[3];
int N;
 
int sub(int x, int y) {
    return x > y ? x - y : 0;
}
 
int func(int hp1, int hp2, int hp3) {
    if (!hp1 && !hp2 && !hp3) return 0;
    
    int &ret = dp[hp1][hp2][hp3];
    if (ret != -1return ret;
    ret = INF;
 
    if (hp1) {
        ret = min(ret, func(sub(hp1, 9), sub(hp2, 3), sub(hp3, 1)) + 1);
        ret = min(ret, func(sub(hp1, 9), sub(hp2, 1), sub(hp3, 3)) + 1);
    }
 
    if (hp2) {
        ret = min(ret, func(sub(hp1, 3), sub(hp2, 9), sub(hp3, 1)) + 1);
        ret = min(ret, func(sub(hp1, 1), sub(hp2, 9), sub(hp3, 3)) + 1);
    }
 
    if (hp3) {
        ret = min(ret, func(sub(hp1, 3), sub(hp2, 1), sub(hp3, 9)) + 1);
        ret = min(ret, func(sub(hp1, 1), sub(hp2, 3), sub(hp3, 9)) + 1);
    }
 
    return ret;
}
 
void input() {
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> list[i];
    }
 
    memset(dp, -1sizeof(dp));
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    cout << func(list[0], list[1], list[2]) << '\n';
 
    return 0;
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 1563 개근상  (0) 2021.06.02
boj 10800 컬러볼  (0) 2021.04.09
boj 1520 내리막 길  (0) 2021.03.28
boj 1937 욕심쟁이 판다  (0) 2021.03.28
boj 2201 이친수 찾기  (2) 2021.03.24

www.acmicpc.net/problem/1520

 

1520번: 내리막 길

첫째 줄에는 지도의 세로의 크기 M과 가로의 크기 N이 빈칸을 사이에 두고 주어진다. 이어 다음 M개 줄에 걸쳐 한 줄에 N개씩 위에서부터 차례로 각 지점의 높이가 빈 칸을 사이에 두고 주어진다.

www.acmicpc.net

emoney96.tistory.com/189

 

boj 1937 욕심쟁이 판다

www.acmicpc.net/problem/1937 1937번: 욕심쟁이 판다 n*n의 크기의 대나무 숲이 있다. 욕심쟁이 판다는 어떤 지역에서 대나무를 먹기 시작한다. 그리고 그 곳의 대나무를 다 먹어 치우면 상, 하, 좌, 우 중

emoney96.tistory.com

이 문제와 비슷한 풀이입니다.

dfs + dp를 사용하며

위의 문제는 칸의 수를 세어주었지만 이 문제는 (0, 0)에서 (N - 1, M - 1)까지 도달할 수 있는 경우의 수를 세어주는 문제입니다.

 

현재 칸보다 높이가 더 낮은 지점으로만 이동합니다.

(N - 1, M - 1)에 도달하면 1을 리턴하고 이미 방문한 좌표에는 해당 좌표의 dp[x][y]를 리턴해줍니다.

 

 

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
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int list[][] = new int[500][500];
    static int dp[][] = new int[500][500];
    static int direct[][] = { { 01 }, { 10 }, { 0-1 }, { -10 } };
    static int N, M;
 
    static int func(int x, int y) {
        if (x == N - 1 && y == M - 1)
            return 1;
        if (dp[x][y] != -1)
            return dp[x][y];
        dp[x][y] = 0;
 
        for (int i = 0; i < 4; i++) {
            int nx = x + direct[i][0];
            int ny = y + direct[i][1];
 
            if (nx < 0 || ny < 0 || nx >= N || ny >= M)
                continue;
            if (list[x][y] <= list[nx][ny])
                continue;
 
            dp[x][y] += func(nx, ny);
        }
        
        return dp[x][y];
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        for (int i = 0; i < N; i++) {
            Arrays.fill(dp[i], -1);
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < M; j++) {
                list[i][j] = Integer.parseInt(st.nextToken());
            }
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        System.out.println(func(00));
    }
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 10800 컬러볼  (0) 2021.04.09
boj 12869 뮤탈리스크  (0) 2021.04.04
boj 1937 욕심쟁이 판다  (0) 2021.03.28
boj 2201 이친수 찾기  (2) 2021.03.24
boj 2228 구간 나누기  (0) 2021.03.21

www.acmicpc.net/problem/1937

 

1937번: 욕심쟁이 판다

n*n의 크기의 대나무 숲이 있다. 욕심쟁이 판다는 어떤 지역에서 대나무를 먹기 시작한다. 그리고 그 곳의 대나무를 다 먹어 치우면 상, 하, 좌, 우 중 한 곳으로 이동을 한다. 그리고 또 그곳에서

www.acmicpc.net

판다는 4방향으로 다닐 수 있지만 대나무의 양이 현재보다 더 많은 곳으로 가야합니다.

완전 탐색을 하면 답을 찾을 수는 있지만 N의 범위가 500까지이므로 시간초과가 날 것입니다.

 

따라서 이 문제는 dfs + dp 문제입니다.

이미 팬더가 지나간 길에 대해서 다시 탐색할 필요가 없으므로 dp를 사용합니다.

dp[x][y] : (x, y)를 시작점으로 판다가 최대한 살 수 있는 일수

 

dfs에 dp가 추가된 간단한 문제였습니다.

 

 

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.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int list[][] = new int[501][501];
    static int dp[][] = new int[501][501];
    static int direct[][] = { { 01 }, { 10 }, { 0-1 }, { -10 } };
    static int N, ans;
 
    static int dfs(int x, int y) {
        if (dp[x][y] != -1)
            return dp[x][y];
        dp[x][y] = 1;
 
        for (int i = 0; i < 4; i++) {
            int nx = x + direct[i][0];
            int ny = y + direct[i][1];
 
            if (nx < 0 || ny < 0 || nx >= N || ny >= N)
                continue;
            if (list[x][y] >= list[nx][ny])
                continue;
 
            dp[x][y] = Math.max(dp[x][y], dfs(nx, ny) + 1);
        }
 
        ans = Math.max(ans, dp[x][y]);
        return dp[x][y];
    }
 
    static void func() {
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (dp[i][j] != -1)
                    continue;
                dfs(i, j);
            }
        }
        System.out.println(ans);
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        for (int i = 0; i < N; i++) {
            Arrays.fill(dp[i], -1);
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < N; j++) {
                list[i][j] = Integer.parseInt(st.nextToken());
            }
        }
 
    }
 
    public static void main(String[] args) throws Exception {
        input();
        func();
    }
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 12869 뮤탈리스크  (0) 2021.04.04
boj 1520 내리막 길  (0) 2021.03.28
boj 2201 이친수 찾기  (2) 2021.03.24
boj 2228 구간 나누기  (0) 2021.03.21
boj 1695 팰린드롬 만들기  (0) 2021.03.20

www.acmicpc.net/problem/2201

 

2201번: 이친수 찾기

0과 1로만 이루어진 수를 이진수라 한다. 이러한 이진수 들 특별한 성질을 갖는 것들이 있는데, 이들을 이친수(pinary number)라 한다. 이친수는 다음의 성질을 만족한다. 이친수는 0으로 시작하지 않

www.acmicpc.net

 

dp[length] : 길이가 length인 이친수의 시작 번호

 

1. 이친수는 0으로 시작하지 않는다.

2. 이친수에서는 1이 두 번 연속으로 나타나지 않는다. 즉, 11은 이친수가 아니다.

위 조건을 고려하여 길이당 이친수의 갯수는 피보나치 수열로 구할 수 있습니다.

 

저는 이를 이용하여 dp에 각 길이당 시작하는 번호를 담아놓습니다.

dp[1] = 1, dp[2] = 2, dp[3]= 3, dp[4] = 5, dp[5]=8, dp[6] = 13, ... 이런식으로 되고,

 

그리고 입력받은 N의 길이를 구합니다.

길이가 i일 때의 시작번호와 비교를 하여 dp[i] > N이면 length는 i - 1입니다.

 

그리고 length만큼 반복문을 돌려주면서 두 가지의 경우를 확인합니다.

1. dp[length] <= N이면 (번호 N이 length의 시작점보다 뒤에 있으면)

 => 1을 출력하고 N을 N의 시작번호만큼 빼줍니다.

 

2. dp[length] > N이면 (번호 N이 length의 시작점보다 앞에 있으면)

 => 0을 출력합니다.

 

 

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
#include <iostream>
using namespace std;
typedef long long ll;
 
ll dp[91], N;
 
void init() {
    dp[1= 1;
    dp[2= 2;
    for (int i = 3; i <= 90; i++) {
        dp[i] = dp[i - 1+ dp[i - 2];
    }
}
 
void solve() {
    int length = 0;
    for (int i = 1; i <= 90; i++) {
        if (dp[i] > N) {
            length = i - 1;
            break;
        }
    }
 
    while (length) {
        if (dp[length] <= N) {
            cout << 1;
            N -= dp[length];
        }
        else {
            cout << 0;
        }
        length--;
    }
    cout << '\n';
}
 
void input() {
    cin >> N;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    init();
    input();
    solve();
 
    return 0;
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 1520 내리막 길  (0) 2021.03.28
boj 1937 욕심쟁이 판다  (0) 2021.03.28
boj 2228 구간 나누기  (0) 2021.03.21
boj 1695 팰린드롬 만들기  (0) 2021.03.20
boj 9177 단어 섞기  (0) 2021.03.19

www.acmicpc.net/problem/2228

 

2228번: 구간 나누기

N(1≤N≤100)개의 수로 이루어진 1차원 배열이 있다. 이 배열에서 M(1≤M≤⌈(N/2)⌉)개의 구간을 선택해서, 구간에 속한 수들의 총 합이 최대가 되도록 하려 한다. 단, 다음의 조건들이 만족되어야

www.acmicpc.net

이 문제는 조건을 잘 확인해야 합니다.

1. 각 구간은 한 개 이상의 연속된 수들로 이루어진다.

2. 서로 다른 두 구간끼리 겹쳐있거나 인접해 있어서는 안 된다.

3. 정확히 M개의 구간이 있어야 한다. M개 미만이어서는 안 된다

6 2
-1
3
1
2
4
-1

위 예제에서의 답은 {3}과 {2, 4}를 선택하여 9입니다.

 

dp[idx][cnt] = 구간의 시작인덱스가 idx이고 cnt번째 구간일 때의 최댓값>

idx가 구간의 시작점이고, 구간의 끝은 반복문을 통해 구하였습니다.

 

구간의 끝을 구하기 전에 idx번째 수를 선택하지 않을 수도 있기때문에 먼저 구해주었습니다.

idx ~ i의 구간을 골랐으면 다음 구간은 이 구간과 인접해있으면 안되기때문에 i+2를 넘겨주었습니다.

 

정확히 M개를 골랐으면 합의 최댓값을 갱신, 남은 배열에서 M개의 구간을 못 고르는 경우 MIN값을 리턴하였습니다.

 

 

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
#include <iostream>
#include <algorithm>
#define INF 1000000000
using namespace std;
 
int list[101], sum[101], dp[101][51];
int N, M;
 
int func(int idx, int cnt) {
    if (cnt == M) return 0;
    if ((N - idx + 2/ 2 < M - cnt) return -INF;
 
    int &ret = dp[idx][cnt];
    if (ret != -INF) return ret;
    ret = func(idx + 1, cnt);
 
    for (int i = idx; i <= N; i++) {
        ret = max(ret, func(i + 2, cnt + 1+ sum[i] - sum[idx - 1]);
    }
 
    return ret;
}
 
void init() {
    for (int i = 0; i <= N; i++) {
        for (int j = 0; j <= M; j++) {
            dp[i][j] = -INF;
        }
    }
}
 
void input() {
    cin >> N >> M;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
        sum[i] = sum[i - 1+ list[i];
    }
    init();
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    cout << func(10<< '\n';
 
    return 0;
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 1937 욕심쟁이 판다  (0) 2021.03.28
boj 2201 이친수 찾기  (2) 2021.03.24
boj 1695 팰린드롬 만들기  (0) 2021.03.20
boj 9177 단어 섞기  (0) 2021.03.19
boj 10942 팰린드롬?  (0) 2021.03.12

www.acmicpc.net/problem/1695

 

1695번: 팰린드롬 만들기

앞에서 뒤로 보나, 뒤에서 앞으로 보나 같은 수열을 팰린드롬 이라고 한다. 예를 들어 {1}, {1, 2, 1}, {1, 2, 2, 1}과 같은 수열은 팰린드롬 이지만, {1, 2, 3}, {1, 2, 3, 2} 등은 팰린드롬이 아니다. 한 수열

www.acmicpc.net

입력으로 주어진 수열에 무작위로 수를 끼워넣어 팰린드롬으로 만드는데 최소 몇개의 수를 끼워 넣으면 되는지 구하는 문제입니다.

 

이 문제는 간단하게 주어진 수열과 뒤집은 수열의 lcs를 구한 후 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
38
#include <iostream>
#define max(a, b) {a > b ? a : b}
using namespace std;
 
int dp[2][5001], list[5001];
int N;
 
void func() {
    int t = 0;
    for (int i = N; i >= 1; i--) {
        for (int j = 1; j <= N; j++) {
            if (list[i] == list[j]) {
                dp[t][j] = dp[1 - t][j - 1+ 1;
            }
            else dp[t][j] = max(dp[1 - t][j], dp[t][j - 1]);
        }
        t = 1 - t;
    }
    
    cout << N - dp[1 - t][N] << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 2201 이친수 찾기  (2) 2021.03.24
boj 2228 구간 나누기  (0) 2021.03.21
boj 9177 단어 섞기  (0) 2021.03.19
boj 10942 팰린드롬?  (0) 2021.03.12
boj 17404 RGB거리 2  (0) 2021.02.28

www.acmicpc.net/problem/9177

 

9177번: 단어 섞기

세 개의 단어가 주어졌을때, 꿍은 첫 번째 단어와 두 번째 단어를 섞어서 세 번째 단어를 만들 수 있는지 궁금해졌다. 첫 번째와 두 번째 단어는 마음대로 섞어도 되지만 원래의 순서는 섞여서는

www.acmicpc.net

str1 : 첫 번째 단어

str2 : 두 번째 단어

result : 세 번째 단어

 

dp[idx1][idx2] : 현재 확인하려는 알파벳이 str1[idx1], str2[idx2]일 때 result를 만들 수 있는지 여부

 

재귀를 통해 두 단어와 세 번째 단어를 비교할 인덱스(idx1, idx2, idx)를 넘겨줍니다.

str1[idx1] == result[idx]이면 첫 번째 단어와 세 번째 단어의 인덱스를 1 증가(idx1 + 1, idx2, idx + 1)

str2[idx2] == result[idx]이면 두 번째 단어와 세 번째 단어의 인덱스를 1 증가(idx1, idx2 + 1, idx + 1)

를 확인해줍니다.

 

인덱스의 조합이 중복으로 나오기때문에 시간적 손해를 줄이기위해 dp를 사용하였습니다.

 

 

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
#include <iostream>
#include <string>
#include <cstring>
#define max(a, b) {a > b ? a : b}
using namespace std;
 
string str1, str2, result;
int dp[201][201];
int size1, size2, rsize;
 
int func(int idx1, int idx2, int idx) {
    if (idx == rsize) return 1;
 
    int &ret = dp[idx1][idx2];
 
    if (ret != -1return ret;
    ret = 0;
 
    if (idx1 < size1 && str1[idx1] == result[idx]) ret = func(idx1 + 1, idx2, idx + 1);
    if (idx2 < size2 && str2[idx2] == result[idx]) ret = max(ret, func(idx1, idx2 + 1, idx + 1));
 
    return ret;
}
 
void input() {
    cin >> str1 >> str2 >> result;
    size1 = str1.size();
    size2 = str2.size();
    rsize = result.size();
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    int tc;
    cin >> tc;
    for (int t = 1; t <= tc; t++) {
        memset(dp, -1sizeof(dp));
        cout << "Data set " << t << ": ";
        input();
        if (func(000)) cout << "yes\n";
        else cout << "no\n";
    }
 
 
    return 0;
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 2228 구간 나누기  (0) 2021.03.21
boj 1695 팰린드롬 만들기  (0) 2021.03.20
boj 10942 팰린드롬?  (0) 2021.03.12
boj 17404 RGB거리 2  (0) 2021.02.28
boj 2616 소형기관차  (0) 2021.02.27

www.acmicpc.net/problem/10942

 

10942번: 팰린드롬?

총 M개의 줄에 걸쳐 홍준이의 질문에 대한 명우의 답을 입력으로 주어진 순서에 따라서 출력한다. 팰린드롬인 경우에는 1, 아닌 경우에는 0을 출력한다.

www.acmicpc.net

팰린드롬이란 앞에서 읽을 때와 뒤에서 읽을 때와 같은 것을 의미합니다.

먼저 양쪽 끝을 기준으로 하나씩 확인하는 방법입니다.

양쪽 끝을 기준으로 한 칸씩 중간으로 이동하면서 확인하는 방법입니다. 이를 이용하면 쉽게 팰린드롬인지 파악할 수 있습니다.

 

하지만 문제에서 입력으로 x, y가 주어지며 이는 x번째 수 ~ y번째 수로 구성된 수열이 팰린드롬인지 묻는 쿼리가 M개 주어지며 1<= M <= 1000000입니다.

수열의 크기가 N(1 <= N <= 2000)이므로 O(NM)의 시간이 소요되며 위의 방법으로는 당연히 시간초과가 발생합니다.

 

 

 

다른 방법으로는 dp를 이용하는 방법입니다.

주어진 수열의 각 구간의 팰린드롬 여부를 미리 구해놓고, 쿼리가 주어지면 저장된 값을 출력만 하면되는 방법입니다.

각 구간의 팰린드롬 여부는 O(N^2)시간에 해결할 수 있고, 쿼리가 주어지면 O(1)시간에 해결할 수 있습니다.

그러면 총 시간복잡도는 O(N^2)가 되므로 문제해결이 가능합니다.

 

 

dp[x][y]: x번째 수 ~ y번째 수로 구성된 수열이 팰린드롬인지 여부

 

[dp[x][y]이 팰린드롬인지 확인하는 방법]

수열의 크기가 작은 부분에서 큰 부분으로 확대시키면서 팰린드롬 여부를 구해줍니다.

1. 길이가 1인 수열 ex) 1

2. 길이가 2인 수열 ex) 11

3. 길이가 3 이상의 수열 ex) 121, 1221, 12321

============================================================

1, 2번은 직접 비교하여 구할 수 있습니다.

3번은 아래의 방법을 사용합니다.

 

1. 팰린드롬인 경우

1. 2332의 왼쪽 끝의 숫자(2)와 오른쪽 끝의 숫자(2)가 2로 같습니다.

2. 양 끝 숫자들로 둘러쌓인 또다른 부분수열인 33역시 팰린드롬입니다.

이 두가지가 모두 만족하여 2332는 팰린드롬입니다.

 

 

2 - 1. 팰린드롬이 아닌 경우

1. 2232의 왼쪽 끝의 숫자(2)와 오른쪽 끝의 숫자(2)가 2로 같습니다.

2. 양 끝 숫자들로 둘러쌓인 또다른 부분수열인 23은 팰린드롬이 아닙니다.

이 두가지 중 2번이 만족하지 않아 2232는 팰린드롬이 아닙니다.

 

 

2 - 2. 팰린드롬이 아닌 경우

1. 2331의 왼쪽 끝의 숫자(2)와 오른쪽 끝의 숫자(1)이 다릅니다.

2. 양 끝 숫자들로 둘러쌓인 또다른 부분수열인 33은 팰린드롬입니다.

이 두가지 중 1번이 만족하지 않아 2331은 팰린드롬이 아닙니다.

 

위의 경우를 고려하여 구현하면 되겠습니다!

 

 

[C++]

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
#include <iostream>
using namespace std;
 
int list[2001], dp[2001][2001];
int N, M;
 
void solve() {
    int x, y;
    while (M--) {
        cin >> x >> y;
        cout << dp[x][y] << '\n';
    }
}
 
void func() {
    for (int i = 2; i < N; i++) {
        for (int j = 1; j <= N - i; j++) {
            if (dp[j + 1][j + i - 1&& (list[j] == list[j + i])) {
                dp[j][j + i] = 1;
            }
        }
    }
}
 
void input() {
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> list[i];
        dp[i][i] = 1;
        if (list[i - 1== list[i]) dp[i - 1][i] = 1;
    }
    cin >> M;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
    solve();
 
    return 0;
}
cs

 

 

[Java]

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
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int dp[][] = new int[2001][2001];
    static int list[] = new int[2001];
    static int N, M;
 
    static void solve() throws Exception {
        StringBuffer sb = new StringBuffer();
        int l, r;
        st = new StringTokenizer(br.readLine());
        M = Integer.parseInt(st.nextToken());
        while (M-- > 0) {
            st = new StringTokenizer(br.readLine());
            l = Integer.parseInt(st.nextToken());
            r = Integer.parseInt(st.nextToken());
 
            sb.append(dp[l][r]).append("\n");
        }
 
        System.out.println(sb.toString());
    }
 
    static void func() {
        for (int i = 2; i < N; i++) {
            for (int j = 1; j + i <= N; j++) {
                if (list[j] == list[j + i] && dp[j + 1][j + i - 1== 1) {
                    dp[j][j + i] = 1;
                }
            }
        }
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
 
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= N; i++) {
            list[i] = Integer.parseInt(st.nextToken());
 
            dp[i][i] = 1;
            if (list[i - 1== list[i])
                dp[i - 1][i] = 1;
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        func();
        solve();
    }
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 1695 팰린드롬 만들기  (0) 2021.03.20
boj 9177 단어 섞기  (0) 2021.03.19
boj 17404 RGB거리 2  (0) 2021.02.28
boj 2616 소형기관차  (0) 2021.02.27
boj 11049 행렬 곱셈 순서  (0) 2021.02.20

www.acmicpc.net/problem/17404

 

17404번: RGB거리 2

첫째 줄에 집의 수 N(2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나

www.acmicpc.net

 

www.acmicpc.net/problem/1149

 

1149번: RGB거리

첫째 줄에 집의 수 N(2 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 각 집을 빨강, 초록, 파랑으로 칠하는 비용이 1번 집부터 한 줄에 하나씩 주어진다. 집을 칠하는 비용은 1,000보다 작거나

www.acmicpc.net

위의 문제에서 1번 집과 N번 집도 이웃이라는 조건이 추가되었습니다.

 

저는 1번 집에 칠할 색을 고정시켜놓고 모든 집을 칠하는 비용의 최솟값을 구하였습니다.

색이 3가지이므로 최솟값은 3번 구하게 되며, 1번 집이 빨강일 경우, 초록일 경우, 파랑일 경우를 따로 구해줍니다.

solve함수는 위의 문제와 동일하게 돌아갑니다.

 

1번 집이 빨강일 경우에는 N번 집이 초록, 파랑을 골랐을 때의 최솟값

1번 집이 초록일 경우에는 N번 집이 빨강, 파랑을 골랐을 때의 최솟값

1번 집이 파랑일 경우에는 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
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.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int list[][], dp[][];
    static int N, ans;
 
    static void solve() {
        for (int i = 2; i <= N; i++) {
            dp[i][0= Math.min(dp[i - 1][1], dp[i - 1][2]) + list[i][0];
            dp[i][1= Math.min(dp[i - 1][0], dp[i - 1][2]) + list[i][1];
            dp[i][2= Math.min(dp[i - 1][0], dp[i - 1][1]) + list[i][2];
        }
    }
 
    static void func() {
        dp[1][0= list[1][0];
        dp[1][1= 1001;
        dp[1][2= 1001;
        solve();
        ans = Math.min(dp[N][1], dp[N][2]);
 
        dp[1][0= 1001;
        dp[1][1= list[1][1];
        dp[1][2= 1001;
        solve();
        ans = Math.min(ans, Math.min(dp[N][0], dp[N][2]));
 
        dp[1][0= 1001;
        dp[1][1= 1001;
        dp[1][2= list[1][2];
        solve();
        ans = Math.min(ans, Math.min(dp[N][0], dp[N][1]));
        
        System.out.println(ans);
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        list = new int[N + 1][3];
        dp = new int[N + 1][3];
 
        for (int i = 1; i <= N; i++) {
            st = new StringTokenizer(br.readLine());
            list[i][0= Integer.parseInt(st.nextToken());
            list[i][1= Integer.parseInt(st.nextToken());
            list[i][2= Integer.parseInt(st.nextToken());
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        func();
    }
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 9177 단어 섞기  (0) 2021.03.19
boj 10942 팰린드롬?  (0) 2021.03.12
boj 2616 소형기관차  (0) 2021.02.27
boj 11049 행렬 곱셈 순서  (0) 2021.02.20
boj 1915 가장 큰 정사각형  (0) 2021.02.20

www.acmicpc.net/problem/2616

 

2616번: 소형기관차

첫째 줄에 기관차가 끌고 가던 객차의 수가 입력된다. 그 수는 50,000 이하이다. 둘째 줄에는 기관차가 끌고 가던 객차에 타고 있는 손님의 수가 1번 객차부터 차례로 입력된다. 한 객차에 타고 있

www.acmicpc.net

객차에 타고있는 손님의 수가 주어지고, 소형기관차가 최대로 끌 수 있는 객차의 수가 주어집니다.

 

저의 솔루션입니다.

dp[idx][cnt] : idx의 위치에서 cnt번째 소형기관차에서 끌 수 있는 최대 손님 수

 

객차에 타고있는 손님의 수를 누적합으로 미리 계산합니다.

func함수에서 가지치기 해야할 것

1. 현재 idx부터 모든 객차를 고를 수 없는 경우

(ex) N = 7, M = 2이고 첫번째 기관차인데 idx가 3인 경우 => 3 - 1 + M * 3 > N)

2. 이미 3개모두 골랐을 경우 (cnt == 0)

 

dp[idx][cnt] != -1 이면 값이 구해진 상태이므로 dp[idx][cnt]를 바로 리턴합니다.

dp[idx][cnt] 값은 현재idx를 고르는 경우와 안고르고 다음idx로 넘겨주는 경우의 최댓값을 구해주시면 됩니다.

현재idx를 고르는 경우에는 구해놨던 누적합을 이용하여 합을 더해주고 func(idx + M, cnt - 1)을 호출

안고르는 경우에는 바로 func(idx + 1, cnt)를 호출해주시면 됩니다.

 

 

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
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int list[] = new int[50001];
    static int dp[][] = new int[50001][4];
    static int N, M;
 
    static void init() {
        for (int i = 1; i <= N; i++)
            Arrays.fill(dp[i], -1);
    }
 
    static int func(int idx, int cnt) {
        if (idx + M * cnt - 1 > N)
            return 0;
        if (cnt == 0)
            return 0;
 
        if (dp[idx][cnt] != -1)
            return dp[idx][cnt];
 
        dp[idx][cnt] = 0;
 
        dp[idx][cnt] = Math.max(func(idx + 1, cnt), func(idx + M, cnt - 1+ list[idx + M - 1- list[idx - 1]);
 
        return dp[idx][cnt];
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        init();
 
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= N; i++) {
            list[i] = Integer.parseInt(st.nextToken());
            list[i] += list[i - 1];
        }
        st = new StringTokenizer(br.readLine());
        M = Integer.parseInt(st.nextToken());
    }
 
    public static void main(String[] args) throws Exception {
        input();
        System.out.println(func(13));
    }
}
cs

'algorithm > dp' 카테고리의 다른 글

boj 10942 팰린드롬?  (0) 2021.03.12
boj 17404 RGB거리 2  (0) 2021.02.28
boj 11049 행렬 곱셈 순서  (0) 2021.02.20
boj 1915 가장 큰 정사각형  (0) 2021.02.20
boj 1912 연속합  (0) 2021.02.18

+ Recent posts