www.acmicpc.net/problem/11049

 

11049번: 행렬 곱셈 순서

첫째 줄에 입력으로 주어진 행렬을 곱하는데 필요한 곱셈 연산의 최솟값을 출력한다. 정답은 231-1 보다 작거나 같은 자연수이다. 또한, 최악의 순서로 연산해도 연산 횟수가 231-1보다 작거나 같

www.acmicpc.net

N * M 행렬과 M * K 행렬을 곱할때는 N * M * K번의 연산이 필요합니다.

이 공식을 이용해서 여러개의 행렬을 곱하는데 필요한 곱셈 연산의 수를 최소로 해야합니다.

입력으로는 순서대로 곱셈이 가능하게 주어지므로 순서대로 연산을 할 수 있습니다.

 

저는 3중for문을 사용하였습니다.

i는 간격, x는 시작 인덱스, y는 중간 인덱스, k는 끝 인덱스를 나타내었습니다.

dp[x][k]는 dp[x][y] + dp[y + 1][k]

즉, (x ~ y번째 행렬을 곱할 때 연산의 최솟값) + (y + 1 ~ k번째 행렬을 곱할 때 연산의 최솟값) 에다가

x ~ y번째가 합쳐있는 행렬과 y + 1 ~ k번째가 합쳐있는 행렬을 곱할 때 필요한 연산을 더해주어야합니다.

 

그러면 점화식은 dp[x][k] = min(dp[x][k], dp[x][y] + dp[y + 1][k] + list[x].first * list[y].second * list[k].secod) 가 될 수 있습니다.

출력은 dp[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
#include <iostream>
#include <algorithm>
using namespace std;
 
pair<intint> list[501];
int dp[501][501];
int N;
 
void func() {
    for (int i = 1; i < N; i++) {
        for (int x = 1; x + i <= N; x++) {
            int k = x + i;
            for (int y = x; y < k; y++) {
                if (!dp[x][y])
                    dp[x][k] = dp[x][y] + dp[y + 1][k] + list[x].first * list[y].second * list[k].second;
                else
                    dp[x][k] = min(dp[x][k], dp[x][y] + dp[y + 1][k] + list[x].first * list[y].second * list[k].second);
            }
        }
    }
 
    cout << dp[1][N] << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> list[i].first >> list[i].second;
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
    
    input();
    func();
 
    return 0;
}
cs

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

boj 17404 RGB거리 2  (0) 2021.02.28
boj 2616 소형기관차  (0) 2021.02.27
boj 1915 가장 큰 정사각형  (0) 2021.02.20
boj 1912 연속합  (0) 2021.02.18
boj 10211 Maximum Subarray  (0) 2021.02.18

www.acmicpc.net/problem/1915

 

1915번: 가장 큰 정사각형

첫째 줄에 n, m(1 ≤ n, m ≤ 1,000)이 주어진다. 다음 n개의 줄에는 m개의 숫자로 배열이 주어진다.

www.acmicpc.net

이런식으로 배열이 주어지면 1로만 이루어진 가장 큰 정사각형의 넓이를 구하는 문제입니다.

 

우선 2 * 2의 정사각형의 예로 들면

여기서 맨 오른쪽 아래(2, 2)를 기준으로 왼쪽(2, 1), 위(1, 2), 왼쪽 위(1, 1)의 수가 모두 1이기때문에 정사각형이 됩니다.

여기서 dp[i][j]의 값은 dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]의 값들로 인해 결정이 되는것을 알 수 있습니다.

 

 

위의 그림은 dp[i - 1][j - 1]이 0이므로 정사각형이 아닙니다.

 

여기서 도출되는 점화식은 dp[i][j] += min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])입니다.

이 점화식은 현재 좌표의 값이 1일 경우에만 적용됩니다. (0일 경우에는 그냥 넘겨줍니다)

계산 과정에서 ans를 계속 갱신시켜줘서 마지막에 넓이를 출력해주시면 됩니다.

 

 

[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
#include <iostream>
#include <algorithm>
using namespace std;
 
int dp[1001][1001];
int N, M, ans;
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    char ch;
    cin >> N >> M;
 
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            cin >> ch;
            dp[i][j] = ch - '0';
            if (dp[i][j]) dp[i][j] += min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1]));
            ans = max(ans, dp[i][j]);
        }
    }
 
    cout << ans * ans << '\n';
 
    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
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[1001][1001];
    static int N, M, ans;
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
 
        for (int i = 1; i <= N; i++) {
            st = new StringTokenizer(br.readLine());
            char ch[] = st.nextToken().toCharArray();
            for (int j = 1; j <= M; j++) {
                dp[i][j] = ch[j - 1- '0';
 
                if (dp[i][j] == 1) {
                    dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])) + 1;
                    ans = Math.max(ans, dp[i][j]);
                }
            }
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        System.out.println(ans * ans);
    }
}
cs

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

boj 2616 소형기관차  (0) 2021.02.27
boj 11049 행렬 곱셈 순서  (0) 2021.02.20
boj 1912 연속합  (0) 2021.02.18
boj 10211 Maximum Subarray  (0) 2021.02.18
boj 10826 피보나치 수 4  (0) 2021.02.08

www.acmicpc.net/problem/1912

 

1912번: 연속합

첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.

www.acmicpc.net

배열이 주어지면 연속된 수를 선택하여 얻을 수 있는 합을 최대로 해야합니다.

dp를 이용하여 간단하게 해결하였지만 분할정복 기법으로 사용한 소스도 같이 올리겠습니다.

 

우선 dp는 간단합니다.

(i번째 수)와 (i - 1까지의 연속합 최대에 i번째 수를 더한 값) 중 최댓값이 dp[i]가 됩니다.

입력이 음수로 들어오기때문에 ans를 미리 Integer.MIN_VALUE로 최솟값을 만들어 놓고 진행합니다.

합의 최대가 N번째에 온다는 보장이 없기때문에 dp를 구할때마다 갱신해줍니다.

 

 

[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
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[];
    static int N, ans = Integer.MIN_VALUE;
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        dp = new int[N + 1];
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= N; i++) {
            dp[i] = Integer.parseInt(st.nextToken());
            dp[i] = Math.max(dp[i], dp[i - 1+ dp[i]);
            ans = Math.max(ans, dp[i]);
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        System.out.println(ans);
    }
}
 
cs

 

다음은 분할정복 기법입니다. 시간상으로는 dp가 더 빠르지만 오늘 배운내용으로 한번 해보았습니다.

구간 1 ~ N 에서의 최대 연속합을 구합니다.

1. 왼쪽(l ~ m) 구간

2. 오른쪽(m + 1 ~ r)

3. 1과 2 모두 포함하는 부분배열의 합을 모두 구해주시면 됩니다.

 

1, 2번은 이분탐색 방식으로 나눠주시면 되고,

3번을 구할때는 m은 무조건 포함하기 때문에 m부터 왼쪽(l까지)으로 인덱스를 옮겨가며 sum에 더해줍니다.

이 때, 더하면서 leftmax를 계속 갱신시켜줍니다.

그리고 m + 1부터 오른쪽(r까지)으로 인덱스를 옮겨가며 sum에 더해줍니다.

이 때도 마찬가지로 더하면서 rightmax를 계속 갱신시켜줍니다.

 

이 문제의 답은 1, 2, 3의 최댓값이 되겠습니다.

 

 

[분할정복]

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.StringTokenizer;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int list[];
    static int N, ans = Integer.MIN_VALUE;
 
    static int binarysearch(int l, int r) {
        if (l == r)
            return list[l];
 
        int m = (l + r) / 2;
        int leftmax = Integer.MIN_VALUE;
        int rightmax = Integer.MIN_VALUE;
        int sum = 0;
        for (int i = m; i >= l; i--) {
            sum += list[i];
            leftmax = Math.max(leftmax, sum);
        }
 
        sum = 0;
        for (int i = m + 1; i <= r; i++) {
            sum += list[i];
            rightmax = Math.max(rightmax, sum);
        }
 
        return Math.max(leftmax + rightmax, Math.max(binarysearch(l, m), binarysearch(m + 1, r)));
    }
 
    static void func() {
        System.out.println(binarysearch(1, N));
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        list = new int[N + 1];
 
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= N; i++) {
            list[i] = Integer.parseInt(st.nextToken());
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        func();
    }
}
cs

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

boj 11049 행렬 곱셈 순서  (0) 2021.02.20
boj 1915 가장 큰 정사각형  (0) 2021.02.20
boj 10211 Maximum Subarray  (0) 2021.02.18
boj 10826 피보나치 수 4  (0) 2021.02.08
boj 5569 출근 경로  (0) 2021.02.06

www.acmicpc.net/problem/10211

 

10211번: Maximum Subarray

크기 N인 정수형 배열 X가 있을 때, X의 부분 배열(X의 연속한 일부분) 중 각 원소의 합이 가장 큰 부분 배열을 찾는 Maximum subarray problem(최대 부분배열 문제)은 컴퓨터 과학에서 매우 잘 알려져 있

www.acmicpc.net

www.acmicpc.net/problem/1912

 

1912번: 연속합

첫째 줄에 정수 n(1 ≤ n ≤ 100,000)이 주어지고 둘째 줄에는 n개의 정수로 이루어진 수열이 주어진다. 수는 -1,000보다 크거나 같고, 1,000보다 작거나 같은 정수이다.

www.acmicpc.net

위의 문제와 같은문제이지만 N의 범위가 적게 들어옵니다.

저는 dp와 세그먼트 트리 두가지 방법으로 해결하였습니다.

이 문제는 당연히 dp로 해결한 것이 속도가 빠르기 때문에 dp로 접근하시면 됩니다.

세그먼트 트리는 그냥 해본거에요..

근데 이문제는 그냥 브루트포스로 모든경우를 다구해도 풀립니다.. 밑에 소스첨부합니다

 

dp의 점화식은 이전 인덱스까지 더한 dp값에 현재 자신의 값을 더한것과 자신의 값 중 큰 값이되어

dp[i] = max(dp[i - 1] + list[i], list[i]) 가 되겠습니다.

 

 

[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
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 StringBuffer sb = new StringBuffer();
    static int list[], dp[];
    static int N;
 
    static void func() {
        int ans = Integer.MIN_VALUE;
        for (int i = 1; i <= N; i++) {
            dp[i] = Math.max(dp[i - 1+ list[i], list[i]);
            ans = Math.max(ans, dp[i]);
        }
        
        sb.append(ans).append("\n");
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        list = new int[N + 1];
        dp = new int[N + 1];
 
        st = new StringTokenizer(br.readLine());
        for (int i = 1; i <= N; i++) {
            list[i] = Integer.parseInt(st.nextToken());
        }
    }
 
    public static void main(String[] args) throws Exception {
        st = new StringTokenizer(br.readLine());
        int tc = Integer.parseInt(st.nextToken());
        while (tc-- > 0) {
            input();
            func();
        }
        
        System.out.println(sb.toString());
    }
}
cs

 

세그먼트 트리 방법으로는 입력으로 list를 받으면

init함수로 각 구간의 합을 미리 구해놓습니다.

이중 for문으로 (i, i ~ 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
60
61
62
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 StringBuffer sb = new StringBuffer();
    static int tree[] = new int[4004];
    static int list[] = new int[1001];
    static int N;
 
    static int init(int node, int s, int e) {
        if (s == e)
            return tree[node] = list[s];
 
        int m = (s + e) / 2;
        return tree[node] = init(node * 2, s, m) + init(node * 2 + 1, m + 1, e);
    }
 
    static int query(int node, int s, int e, int l, int r) {
        if (s > r || l > e)
            return 0;
        if (l <= s && e <= r)
            return tree[node];
 
        int m = (s + e) / 2;
        return query(node * 2, s, m, l, r) + query(node * 2 + 1, m + 1, e, l, r);
    }
 
    static void func() {
        int ans = Integer.MIN_VALUE;
        for (int i = 1; i <= N; i++) {
            for (int j = i; j <= N; j++) {
                ans = Math.max(ans, query(11, N, i, j));
            }
        }
        
        sb.append(ans).append("\n");
    }
 
    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());
        }
        init(11, N);
    }
 
    public static void main(String[] args) throws Exception {
        st = new StringTokenizer(br.readLine());
        int tc = Integer.parseInt(st.nextToken());
        while (tc-- > 0) {
            input();
            func();
        }
        
        System.out.println(sb.toString());
    }
}
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
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 StringBuffer sb = new StringBuffer();
    static int list[];
    static int N;
 
    static void func() {
        int ans = Integer.MIN_VALUE;
        for (int i = 0; i < N; i++) {
            for (int j = i; j < N; j++) {
                int sum = 0;
                for (int k = i; k <= j; k++) {
                    sum += list[k];
                }
                ans = Math.max(ans, sum);
            }
        }
        sb.append(ans).append("\n");
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        list = new int[N];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) {
            list[i] = Integer.parseInt(st.nextToken());
        }
    }
 
    public static void main(String[] args) throws Exception {
        st = new StringTokenizer(br.readLine());
        int tc = Integer.parseInt(st.nextToken());
        while (tc-- > 0) {
            input();
            func();
        }
 
        System.out.println(sb.toString());
    }
}
cs

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

boj 1915 가장 큰 정사각형  (0) 2021.02.20
boj 1912 연속합  (0) 2021.02.18
boj 10826 피보나치 수 4  (0) 2021.02.08
boj 5569 출근 경로  (0) 2021.02.06
boj 1010 다리 놓기  (0) 2021.02.05

www.acmicpc.net/problem/10826

 

10826번: 피보나치 수 4

피보나치 수는 0과 1로 시작한다. 0번째 피보나치 수는 0이고, 1번째 피보나치 수는 1이다. 그 다음 2번째 부터는 바로 앞 두 피보나치 수의 합이 된다. 이를 식으로 써보면 Fn = Fn-1 + Fn-2 (n ≥ 2)가

www.acmicpc.net

 

점화식 : dp[i] = dp[i - 1] + dp[i - 2]

앞의 두 개의 수를 더한 값이 자신의 값이 됩니다.

 

이 문제는 N이 10000까지 오기때문에 long의 범위를 초과합니다.

자바에는 BigInteger라는 클래스가 있기때문에 이용 사용하여 쉽게 구할 수 있습니다.

 

 

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
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.StringTokenizer;
 
public class Main {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StringTokenizer st;
    static int N;
 
    static void solve() {
        BigInteger dp[] = new BigInteger[10001];
        dp[0= new BigInteger("0");
        dp[1= new BigInteger("1");
        for (int i = 2; i <= N; i++) {
            dp[i] = dp[i - 1].add(dp[i - 2]);
        }
 
        System.out.println(dp[N]);
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
    }
 
    public static void main(String[] args) throws Exception {
        input();
        solve();
    }
}
cs

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

boj 1912 연속합  (0) 2021.02.18
boj 10211 Maximum Subarray  (0) 2021.02.18
boj 5569 출근 경로  (0) 2021.02.06
boj 1010 다리 놓기  (0) 2021.02.05
boj 5557 1학년  (0) 2021.02.05

www.acmicpc.net/problem/5569

 

5569번: 출근 경로

상근이가 사는 도시는 남북 방향으로 도로가 w개, 동서 방향으로 도로가 h개 있다.  남북 방향 도로는 서쪽부터 순서대로 번호가 1, 2, ..., w로 매겨져 있다. 또, 동서 방향 도로는 남쪽부터 순서대

www.acmicpc.net

저는 4차원배열로 dp를 구성하였습니다.

 

dp[x][y][t][chk]

x, y 좌표

t : 현재 진행방향 (0 -> x / 1 -> y)

chk : 방향 변경 가능 여부 (0 -> 변경 불가능 / 1 -> 변경 가능)

 

현재 진행 방향에서 chk = 0이면 현재 진행방향으로 이동해야합니다.

chk = 1이면 현재 진행방향, 다른 방향 모두 탐색합니다.

 

모든 경우의 수를 계산하여 100000으로 나눈 나머지를 출력합니다.

 

 

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
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 final int MOD = 100000;
    static int dp[][][][];
    static int N, M;
 
    static int func(int x, int y, int t, int chk) {
        if (x <= 0 || y <= 0 || x > N || y > M)
            return 0;
 
        if (x == N && y == M)
            return dp[x][y][t][chk] = 1;
 
        if (dp[x][y][t][chk] != -1)
            return dp[x][y][t][chk];
 
        dp[x][y][t][chk] = 0;
        if (t == 1) {
            if (chk == 1) {
                dp[x][y][t][chk] = (dp[x][y][t][chk] + func(x, y + 1, t, chk) + func(x + 1, y, 1 - t, 1 - chk)) % MOD;
            } else {
                dp[x][y][t][chk] = (dp[x][y][t][chk] + func(x, y + 1, t, 1 - chk)) % MOD;
            }
        } else {
            if (chk == 1) {
                dp[x][y][t][chk] = (dp[x][y][t][chk] + func(x + 1, y, t, chk) + func(x, y + 11 - t, 1 - chk)) % MOD;
            } else {
                dp[x][y][t][chk] = (dp[x][y][t][chk] + func(x + 1, y, t, 1 - chk)) % MOD;
            }
        }
 
        return dp[x][y][t][chk];
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        dp = new int[N + 1][M + 1][2][2];
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= M; j++) {
                Arrays.fill(dp[i][j][0], -1);
                Arrays.fill(dp[i][j][1], -1);
            }
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        System.out.println((func(1100+ func(1110)) % MOD);
    }
}
cs

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

boj 10211 Maximum Subarray  (0) 2021.02.18
boj 10826 피보나치 수 4  (0) 2021.02.08
boj 1010 다리 놓기  (0) 2021.02.05
boj 5557 1학년  (0) 2021.02.05
boj 5573 산책  (0) 2021.02.04

www.acmicpc.net/problem/1010

 

1010번: 다리 놓기

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트케이스에 대해 강의 서쪽과 동쪽에 있는 사이트의 개수 정수 N, M (0 < N ≤ M < 30)이 주어진다.

www.acmicpc.net

이항계수 공식을 이용하는 문제입니다.

dp[K][N] = kCn으로 하였고, kCn = (k-1)C(n-1)+(k-1)C(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
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 StringBuffer sb = new StringBuffer();
    static int dp[][] = new int[31][31];
    static int N, M;
 
    static void init() {
        for (int i = 1; i <= 30; i++) {
            dp[i][0= 1;
            dp[0][i] = 1;
        }
 
        for (int i = 1; i <= 30; i++) {
            for (int j = 1; j <= i; j++) {
                dp[i][j] = dp[i - 1][j - 1+ dp[i - 1][j];
            }
        }
    }
 
    public static void main(String[] args) throws Exception {
        init();
        st = new StringTokenizer(br.readLine());
        int tc = Integer.parseInt(st.nextToken());
        int N, K;
        while (tc-- > 0) {
            st = new StringTokenizer(br.readLine());
            N = Integer.parseInt(st.nextToken());
            K = Integer.parseInt(st.nextToken());
            sb.append(dp[K][N] + "\n");
        }
 
        System.out.println(sb.toString());
    }
}
cs

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

boj 10826 피보나치 수 4  (0) 2021.02.08
boj 5569 출근 경로  (0) 2021.02.06
boj 5557 1학년  (0) 2021.02.05
boj 5573 산책  (0) 2021.02.04
boj 17528 Two Machines  (0) 2021.01.28

www.acmicpc.net/problem/5557

 

5557번: 1학년

상근이가 1학년 때, 덧셈, 뺄셈을 매우 좋아했다. 상근이는 숫자가 줄 지어있는 것을 보기만 하면, 마지막 두 숫자 사이에 '='을 넣고, 나머지 숫자 사이에는 '+' 또는 '-'를 넣어 등식을 만들며 놀

www.acmicpc.net

조합과 dp를 이용하여 해결하였습니다.

 

0 ~ N - 2를 조합하여 N - 1번째 값이 나오는 갯수를 출력하는 문제입니다.

단, 더하거나 빼는 과정에서 음수나 20보다 커지면 안됩니다.

저는 배열을 dp[idx][now] => (현재 idx에서 값이 now일 경우의 수)

 

0번 인덱스의 값은 무조건 +로 들어가니 그 다음인 1번 인덱스와, list[0]으로 재귀를 돌려줍니다.

now + list[idx] <= 20 일 경우에만 더한 값을 다음으로 넘겨주고

now - list[idx]>=0 일 경우에만 뺀 값을 다음으로 넘겨줍니다.

 

인덱스가 N - 1이 되면 지금까지 구한 값이 list[N - 1]와 같은지 확인하고 값을 리턴시켜줍니다.

 

 

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
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 long dp[][];
    static int N, list[];
 
    static long func(int idx, int now) {
        if (idx == N - 1) {
            if (now == list[idx])
                return dp[idx][now] = 1;
            return dp[idx][now] = 0;
        }
 
        if (dp[idx][now] != -1)
            return dp[idx][now];
 
        dp[idx][now] = 0;
 
        if (now + list[idx] <= 20)
            dp[idx][now] += func(idx + 1, now + list[idx]);
 
        if (now - list[idx] >= 0)
            dp[idx][now] += func(idx + 1, now - list[idx]);
 
        return dp[idx][now];
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        list = new int[N];
        dp = new long[N][21];
 
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) {
            list[i] = Integer.parseInt(st.nextToken());
        }
    }
 
    static void init() {
        for (int i = 0; i < N; i++) {
            Arrays.fill(dp[i], -1);
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        init();
        System.out.println(func(1, list[0]));
    }
}
cs

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

boj 5569 출근 경로  (0) 2021.02.06
boj 1010 다리 놓기  (0) 2021.02.05
boj 5573 산책  (0) 2021.02.04
boj 17528 Two Machines  (0) 2021.01.28
boj 9657 돌 게임 3  (0) 2021.01.27

www.acmicpc.net/problem/5573

 

5573번: 산책

첫째 줄에 H, W, N이 주어진다. (1 ≤ H,W ≤ 1000, 1 ≤ N ≤ 107) 둘째 줄부터 H개 줄에는 W개 정수가 주어진다. 이 정수는 상근이가 교차로에 써 놓은 문자의 정보이다. 0은 아래쪽을 의미하는 '아', 1은

www.acmicpc.net

1000 * 1000에 10000000번 산책을 하기때문에 시뮬레이션으로 접근하다가는 무조건 시간초과 뜹니다.

저는 방향정보를 dp를 이용하여 N - 1번까지 산책한 이후의 경로를 만들어 놓고 마지막 한 번 산책하는 방식으로 하였습니다.

 

dp에는 N - 1번의 산책동안 (x, y)에 몇 번 지나갔는지 표시하는데 사용하였습니다.

여기서 문제에서 산책하는 규칙이 있습니다.

0은 아래쪽, 1은 오른쪽 방향으로 가며 한 번 어떤 방향으로 가면 그 다음번 산책에서는 반드시 다른 방향으로 이동하게 되어있습니다.

따라서 dp[x][y]는 dp[x-1][y]에서 방문한 횟수의 1/2 + dp[x][y-1]에서 방문한 횟수의 1/2를 한 값입니다.

여기서 추가해야할 것이 dp[x-1][y], dp[x][y-1]이 홀수일 때 방향에 따라서도 방문횟수가 추가될 수 있습니다.

 

(x-1, y) 즉, 위쪽 방향에서 밑쪽으로 지나가려면 경로는 아래쪽 방향이 되어있어야합니다.

(x, y-1) 즉, 왼쪽 방향에서 오른쪽으로 지나가려면 경로는 오른쪽 방향이 되어있어야합니다.

위의 두가지를 추가하기 위해 처음에 받은 list[x-1][y], list[x][y-1]을 확인해야합니다.

 

(x-1, y)의 경우 처음 방향인 list[x-1][y]이 0이면 홀수일 때 1을 추가해줍니다.

(x, y-1)의 경우 처음 방향인 list[x][y-1]이 1이면 짝수일 때 1을 추가해줍니다.

 

이렇게 dp를 다 구하고 나면 이를 이용하여 산책경로를 모두 바꿔야합니다.

dp[x][y]가 짝수인 위치는 방향을 바꿀 필요 없고, 홀수인 위치에만 방향을 바꿔줍니다.

 

마지막으로 x=1, y=1에서부터 맵밖으로 산책을 즐기시면 됩니다.

 

 

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
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[][] = new int[1001][1001];
    static int H, W, N;
 
    static void solve() {
        int x = 1;
        int y = 1;
        while (true) {
            if (list[x][y] == 1)
                y++;
            else
                x++;
 
            if (x > H || y > W)
                break;
        }
 
        System.out.println(x + " " + y);
    }
 
    static void func() {
        int dp[][] = new int[H + 1][W + 1];
        for (int i = 1; i <= H; i++) {
            for (int j = 1; j <= W; j++) {
                if (i == 1 && j == 1) {
                    dp[i][j] = N - 1;
                    continue;
                }
                
                dp[i][j] = dp[i - 1][j] / 2 + dp[i][j - 1/ 2;
 
                if (dp[i - 1][j] % 2 == 1 && list[i - 1][j] == 0)
                    dp[i][j]++;
                if (dp[i][j - 1] % 2 == 1 && list[i][j - 1== 1)
                    dp[i][j]++;
            }
        }
 
        for (int i = 1; i <= H; i++) {
            for (int j = 1; j <= W; j++) {
                if (dp[i][j] % 2 == 0)
                    continue;
 
                list[i][j] = 1 - list[i][j];
            }
        }
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        H = Integer.parseInt(st.nextToken());
        W = Integer.parseInt(st.nextToken());
        N = Integer.parseInt(st.nextToken());
 
        for (int i = 1; i <= H; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 1; j <= W; j++) {
                list[i][j] = Integer.parseInt(st.nextToken());
            }
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        func();
        solve();
    }
}
cs

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

boj 1010 다리 놓기  (0) 2021.02.05
boj 5557 1학년  (0) 2021.02.05
boj 17528 Two Machines  (0) 2021.01.28
boj 9657 돌 게임 3  (0) 2021.01.27
boj 2096 내려가기  (0) 2021.01.22

www.acmicpc.net/problem/17528

 

17528번: Two Machines

스케줄링 최적화 회사인 SOPT 에 완료해야 할 n개의 작업 t1, t2, ..., tn 이 있다. SOPT 회사는 두 대의 머신 A 와 B 를 보유하고 있다. 각 작업 ti를 완료하기 위해 SOPT 는 머신 A 와 B 둘 중에 오직 하나

www.acmicpc.net

2019 icpc 지역예선 L번이고, 2020 icpc 예비소집 문제였습니다.

 

icpc 예선 때 그리디로 접근을 해봤을때 WA를 받아서 결국 못풀었는데 이게 dp라고해서 당황했던 기억이 있습니다.....

 

newdeal123.tistory.com/5

 

[BOJ][백준]DP-17528번 Two Machines

https://www.acmicpc.net/problem/17528 17528번: Two Machines 문제 스케줄링 최적화 회사인 SOPT 에 완료해야 할 n개의 작업 t1, t2, ..., tn 이 있다. SOPT 회사는 두 대의 머신 A 와 B 를 보유하고 있다. 각..

newdeal123.tistory.com

 

이후에 위의 블로그에서 얻은 정보를 바탕으로 2020년 icpc 예비소집때는 AC를 받을 수 있었습니다!

하지만 냅색방법으로 푸는 것이 효율이 더 좋다고 합니다.

저는 냅색을 잘 이해하지 못해서 이해하는데 성공했던 이 방법으로 하였습니다.

 

우선 dp의 구성은

dp[index][해당 기계의 현재 남은 작업량][A기계 or B기계] => 최소시간

이렇게 하였습니다.

 

만약 A 기계가 now만큼의 작업을 수행중일 때 3가지를 고려하여야 합니다.

1. 다음 작업을 A기계가 수행하는 경우

    => 그냥 A의 작업량을 추가하면 됩니다.

2. 다음 작업을 B기계가 수행하는 경우 & now보다 다음 작업량이 많을 경우

    => 작업이 B로 넘어가며, now만큼의 작업을 수행한 후에 next - now를 재귀로 넘겨줍니다.

3. 다음 작업을 B기계가 수행하는 경우 & now보다 다음 작업량일 적을 경우

    => 그대로 A가 작업하며, now-next를 재귀로 넘겨줍니다.

 

이 과정을 반복하시면 되겠습니다.

 

시간이 되면 냅색문제 개념부터 차근차근 봐야겠습니다..

 

 

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
#include <iostream>
#include <algorithm>
#include <cstring>
#define INF 1000000000
using namespace std;
 
int dp[251][62501][2];
int list1[251], list2[251], N;
 
int func(int idx, int now, int t) {
    if (idx == N + 1return now;
 
    if (dp[idx][now][t] != -1return dp[idx][now][t];
    dp[idx][now][t] = INF;
 
    if (t) {
        dp[idx][now][t] = min(dp[idx][now][t], func(idx + 1, now + list1[idx], t));
 
        if (now < list2[idx]) {
            dp[idx][now][t] = min(dp[idx][now][t], func(idx + 1, list2[idx] - now, 1 - t) + now);
        }
        else {
            dp[idx][now][t] = min(dp[idx][now][t], func(idx + 1, now - list2[idx], t) + list2[idx]);
        }
    }
    else {
        dp[idx][now][t] = min(dp[idx][now][t], func(idx + 1, now + list2[idx], t));
 
        if (now < list1[idx]) {
            dp[idx][now][t] = min(dp[idx][now][t], func(idx + 1, list1[idx] - now, 1 - t) + now);
        }
        else {
            dp[idx][now][t] = min(dp[idx][now][t], func(idx + 1, now - list1[idx], t) + list1[idx]);
        }
    }
 
    return dp[idx][now][t];
}
 
void input() {
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> list1[i] >> list2[i];
    }
    memset(dp, -1sizeof(dp));
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    cout << func(100<< '\n';
 
    return 0;
}
cs

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

boj 5557 1학년  (0) 2021.02.05
boj 5573 산책  (0) 2021.02.04
boj 9657 돌 게임 3  (0) 2021.01.27
boj 2096 내려가기  (0) 2021.01.22
boj 1103 게임  (0) 2021.01.22

www.acmicpc.net/problem/9657

 

9657번: 돌 게임 3

상근이가 게임을 이기면 SK를, 창영이가 게임을 이기면 CY을 출력한다.

www.acmicpc.net

dp로 해결할 수 있는 문제입니다.

 

상근이가 게임을 먼저 시작하고, 한 번에 가져갈 수 있는 돌의 갯수는 1개, 3개, 4개 입니다.

 

마지막에 돌은 가져간 사람은 승리하므로 dp[1] = true, dp[3] = true, dp[4] = true를 초기값으로 잡습니다.

그리고 dp[5]부터 dp[N]까지 반복문을 돌립니다.

 

여기서 i번째에서 돌을 가져갔을 때 남아있는 돌의 갯수에 따른 승패여부를 확인해야합니다.

돌을 1개 가져갔을 때 i - 1,

돌을 3개 가져갔을 때 i - 3,

돌을 4개 가져갔을 때 i - 4이므로

dp[i - 1], dp[i - 3], dp[i - 4]의 상황을 모두 체크해야합니다.

 

두 플레이어는 이기기 위해 최선을 다한다고 생각하고,

돌을 가져갔을 때 dp[i - 1], dp[i - 3], dp[i - 4] 모두가 true면 상대방이 무조건 이기는 경우이므로 false입니다.

반대로 dp[i - 1], dp[i - 3], dp[i - 4] 중 하나라도 false인 경우가 있으면 그 경우를 선택하면 무조건 이기므로 true입니다.

 

 

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
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 boolean dp[] = new boolean[1001];
    static int N;
 
    static void func() {
        dp[1= true;
        dp[3= true;
        dp[4= true;
        for (int i = 5; i <= N; i++) {
            if (dp[i - 1&& dp[i - 3&& dp[i - 4])
                dp[i] = false;
            else
                dp[i] = true;
        }
    }
    
    static void print() {
        if(dp[N])
            System.out.println("SK");
        else
            System.out.println("CY");
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
    }
 
    public static void main(String[] args) throws Exception {
        input();
        func();
        print();
    }
}
cs

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

boj 5573 산책  (0) 2021.02.04
boj 17528 Two Machines  (0) 2021.01.28
boj 2096 내려가기  (0) 2021.01.22
boj 1103 게임  (0) 2021.01.22
boj 12852 1로 만들기 2  (0) 2021.01.22

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

 

2096번: 내려가기

첫째 줄에 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 N개의 줄에는 숫자가 세 개씩 주어진다. 숫자는 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 중의 하나가 된다.

www.acmicpc.net

기본적인 dp로 해결가능한 문제입니다.

 

각 칸의 최대/최소는

dp(i,0)의 값은 dp(i-1, 0), dp(i-1, 1)의 최대/최소

dp(i,1)의 값은 dp(i-1, 0), dp(i-1, 1), dp(i-1, 2)의 최대/최소

dp(i,2)의 값은 dp(i-1, 1), dp(i-1, 2)의 최대/최소

에서 해당 칸의 값을 더한 값으로 계산할 수 있습니다.

 

하지만 이 문제는 C++로 해결할 경우에 메모리제한이 4MB, JAVA로 해결할 경우에 256MB이 걸려있습니다.

 

메모리 절약을 위해 슬라이딩 윈도우 기법을 이용하였습니다.

이 문제는 i번째 줄의 값들을 구하기 위해서는 i-1번째 줄의 값만 있으면 됩니다.

따라서 공간은 2*3의 크기만큼 있으면 되고, 인덱스는 0과 1을 반복적으로 참조하는 식으로 하였습니다.

 

 

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
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 maxdp[][], mindp[][];
    static int N, t;
 
    static void print() {
        t = 1 - t;
        int maxans = Math.max(maxdp[t][0], Math.max(maxdp[t][1], maxdp[t][2]));
        int minans = Math.min(mindp[t][0], Math.min(mindp[t][1], mindp[t][2]));
 
        System.out.println(maxans + " " + minans);
    }
 
    static void input() throws Exception {
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        maxdp = new int[2][3];
        mindp = new int[2][3];
        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());
            int a = Integer.parseInt(st.nextToken());
            int b = Integer.parseInt(st.nextToken());
            int c = Integer.parseInt(st.nextToken());
            maxdp[t][0= Math.max(maxdp[1 - t][0], maxdp[1 - t][1]) + a;
            maxdp[t][1= Math.max(maxdp[1 - t][0], Math.max(maxdp[1 - t][1], maxdp[1 - t][2])) + b;
            maxdp[t][2= Math.max(maxdp[1 - t][1], maxdp[1 - t][2]) + c;
 
            mindp[t][0= Math.min(mindp[1 - t][0], mindp[1 - t][1]) + a;
            mindp[t][1= Math.min(mindp[1 - t][0], Math.min(mindp[1 - t][1], mindp[1 - t][2])) + b;
            mindp[t][2= Math.min(mindp[1 - t][1], mindp[1 - t][2]) + c;
 
            
            t = 1 - t;
        }
    }
 
    public static void main(String[] args) throws Exception {
        input();
        print();
    }
}
cs

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

boj 17528 Two Machines  (0) 2021.01.28
boj 9657 돌 게임 3  (0) 2021.01.27
boj 1103 게임  (0) 2021.01.22
boj 12852 1로 만들기 2  (0) 2021.01.22
boj 16195 1, 2, 3 더하기 9  (0) 2021.01.22

+ Recent posts