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

 

1253번: 좋다

첫째 줄에는 수의 개수 N(1 ≤ N ≤ 2,000), 두 번째 줄에는 i번째 수를 나타내는 Ai가 N개 주어진다. (|Ai| ≤ 1,000,000,000, Ai는 정수)

www.acmicpc.net

투 포인터를 이용하여 배열 내의 어떤 수를 다른 두개의 합으로 나타낼 수 있는지 확인합니다.

투 포인터 사용을 위해 배열을 오름차순으로 정렬하고, 0번 인덱스부터 N - 1번 인덱스까지 전부 확인합니다.

초기 값은 left = 0, right = N - 1이며, 합이 list[i]와 같으면 바로 break, 합이 더 크면 right를 1 감소, 합이 더 작으면 left를 1 증가시킵니다.

 

이 문제에서 주의할 점은 "수의 위치가 다르면 값이 같아도 다른 수이다" 부분입니다.

자기 자신과 0을 더하는 경우가 나올 수 있는 left 또는 right가 i와 일치한 경우만 제외하였습니다.

 

 

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 <algorithm>
#define MAX 2000
using namespace std;
 
int list[MAX];
int N;
 
void func() {
    int ans = 0;
    for (int i = 0; i < N; i++) {
        int l = 0;
        int r = N - 1;
        while (l < r) {
            if (l == i) {
                l++;
                continue;
            }
            if (r == i) {
                r--;
                continue;
            }
 
            int sum = list[l] + list[r];
            if (sum == list[i]) {
                ans++;
                break;
            }
            else if (sum > list[i]) {
                r--;
            }
            else {
                l++;
            }
        }
    }
 
    cout << ans << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> list[i];
    }
    sort(list, list + N);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > Two-Pointer' 카테고리의 다른 글

boj 9024 두 수의 합  (0) 2021.10.21
boj 17609 회문  (0) 2021.03.29
boj 2559 수열  (0) 2021.02.12
boj 1806 부분합  (0) 2021.01.22
boj 2003 수들의 합 2  (0) 2021.01.22

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

 

9024번: 두 수의 합

프로그램은 표준입력으로 입력을 받는다. 프로그램 입력은 t 개의 테스트 케이스로 구성된다. 입력의 첫 번째 줄에 테스트 케이스의 개수를 나타내는 정수 t 가 주어진다. 두 번째 줄부터 두 줄

www.acmicpc.net

배열에서 합이 K에 가장 가까운 서로 다른 두 개의 정수 조합의 갯수를 구하는 문제입니다.

이 문제는 투 포인터를 이용하여 해결할 수 있습니다.

 

투 포인터 사용을 위해 배열을 오름차순으로 정렬합니다.

그리고 left = 0, right = N - 1에서 시작합니다.

 

두 정수의 합(list[l] + list[r])이 K에 더 가까우면 합(ans)을 갱신, 갯수를 의미하는 cnt를 1로 초기화합니다.

만약 두 정수의 합과 갱신된 합이 같으면 cnt를 1 증가합니다.

그리고 두 정수의 합이 K보다 크면 값을 작게 해야하므로 right를 1 감소,

두 정수의 합이 K보다 작으면 값을 크게 해야하므로 left를 1 증가시킵니다.

 

최종으로 K에 가장 가까운 두 정수의 조합의 갯수인 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
#include <iostream>
#include <algorithm>
#define MAX 1000000
using namespace std;
 
int list[MAX];
int N, K;
 
void func() {
    int l = 0;
    int r = N - 1;
    int ans = 2e8;
    int cnt = 0;
    while (l < r) {
        int sum = list[l] + list[r];
        if (abs(sum - K) < abs(ans - K)) {
            ans = sum;
            cnt = 1;
        }
        else if (abs(sum - K) == abs(ans - K)) {
            cnt++;
        }
 
        if (sum > K) r--;
        else l++;
    }
 
    cout << cnt << '\n';
}
 
void input() {
    cin >> N >> K;
    for (int i = 0; i < N; i++) {
        cin >> list[i];
    }
    sort(list, list + N);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    int tc;
    cin >> tc;
    while (tc--) {
        input();
        func();
    }
 
    return 0;
}
cs

'algorithm > Two-Pointer' 카테고리의 다른 글

boj 1253 좋다  (0) 2021.10.21
boj 17609 회문  (0) 2021.03.29
boj 2559 수열  (0) 2021.02.12
boj 1806 부분합  (0) 2021.01.22
boj 2003 수들의 합 2  (0) 2021.01.22

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

 

16120번: PPAP

첫 번째 줄에 문자열이 주어진다. 문자열은 대문자 알파벳 P와 A로만 이루어져 있으며, 문자열의 길이는 1 이상 1,000,000 이하이다.

www.acmicpc.net

P는 PPAP 문자열이며, P를 PPAP로 바꾼 문자열 역시 PPAP 문자열입니다.

이 조건과 문자열 주어졌을 때 PPAP 문자열인지 판별하는 문제입니다.

 

우선 문자열의 처음부터 순회를 합니다.

해당 위치의 문자가

'P'이면 cnt를 1 증가합니다.

'A'이면 cnt가 2 이상이고, 다음 문자가 'P'이면 PPAP문자열이므로 PPAP를 P로 변경합니다.

실제 로직으로는 cnt를 1 감소하고, 다음 인덱스가 P인것을 확인했으니 인덱스도 1 증가합니다.

만약 'A'일때 cnt가 2보다 작거나, 마지막 인덱스이거나, 다음 문자가 'P'가 아니면 NP를 출력합니다.

 

순회가 끝났을때 남는 문자열은 P여야하므로 cnt가 1일때만 PPAP를 출력, 아니면 NP를 출력합니다.

 

 

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
#include <iostream>
#include <string>
using namespace std;
 
string str;
int N;
 
void func() {
    int cnt = 0;
    for (int i = 0; i < N; i++) {
        if (str[i] == 'P') cnt++;
        else {
            if (cnt >= 2 && i < N - 1 && str[i + 1== 'P') {
                cnt--;
                i++;
            }
            else {
                cout << "NP\n";
                return;
            }
        }
    }
 
    if (cnt == 1cout << "PPAP\n";
    else cout << "NP\n";
}
 
void input() {
    cin >> str;
    N = str.size();
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 1263 시간 관리  (0) 2024.07.03
boj 18185 라면 사기 (Small)  (0) 2024.07.02
boj 2262 토너먼트 만들기  (0) 2021.10.16
boj 13904 과제  (0) 2021.09.30
boj 1826 연료 채우기  (0) 2021.02.22

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

 

2262번: 토너먼트 만들기

월드시에서는 매년 n명의 사람들이 모여 월드 크래프트라는 게임의 토너먼트 대회를 치른다. 이 게임은 특성상 실력만이 승패를 좌우하기 때문에, 아무리 실력이 엇비슷한 사람이 시합을 치러

www.acmicpc.net

주어지는 랭킹은 중복이 없으며, 랭킹이 높은 사람 (숫자가 낮은 사람)이 무조건 이깁니다.

문제에서 요구하는 것은 이 토너먼트에서 각 경기에 임하는 선수들의 랭킹의 차이의 합이 최소가 되도록 대진을 구성해야합니다.

 

랭킹이 낮을수록 (숫자가 클수록) 랭킹의 차이의 합이 커지므로 랭킹이 낮은 선수는 빠르게 경기를 하는 것이 이득입니다.

따라서 랭킹이 낮은 선수부터 차례대로 본인의 좌우 선수들 중 랭킹이 낮은 선수와 경기를 하게 만들면 랭킹의 차이의 합이 최소가 됩니다.

 

 

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
#include <iostream>
#include <algorithm>
#define MAX 256
using namespace std;
 
int list[MAX];
int N;
 
void func() {
    int ans = 0;
    for (int i = N; i > 1; i--) {
        int j = 0;
        for (; j <= N; j++) {
            if (i == list[j]) break;
        }
 
        ans += (i - max(list[j - 1], list[j + 1]));
 
        for (; j < N; j++) {
            list[j] = list[j + 1];
        }
        N--;
    }
 
    cout << ans << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 0; 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 > Greedy' 카테고리의 다른 글

boj 18185 라면 사기 (Small)  (0) 2024.07.02
boj 16120 PPAP  (0) 2021.10.16
boj 13904 과제  (0) 2021.09.30
boj 1826 연료 채우기  (0) 2021.02.22
boj 8980 택배  (0) 2021.02.16

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

 

13904번: 과제

예제에서 다섯 번째, 네 번째, 두 번째, 첫 번째, 일곱 번째 과제 순으로 수행하고, 세 번째, 여섯 번째 과제를 포기하면 185점을 얻을 수 있다.

www.acmicpc.net

그리디는 많이 안풀어봐서 어렵네요..

이 문제는 정렬 + 그리디 문제입니다.

 

우선 마감일, 과제 점수를 담은 list배열을

  1. 과제 점수가 높은 순 (내림차순)
  2. 마감일이 적게 남은 순 (오름차순)

으로 정렬합니다.

 

그 다음 과제 점수가 가장 높은 0번 인덱스부터 과제를 진행합니다.

여기서 확인해야할 것은 마감일 안에 과제를 할 수 있는지에 대한 여부입니다.

d일부터 1일까지 visit[d]=true인지 확인해서 해당 일에 과제를 했는지 체크합니다.

과제를 했다면 전날 과제를 했는지 체크하는 방식으로 1일까지 확인합니다.

 

과제를 하지않은 날이 있다면 과제를 진행하고 방문체크 해주면 되고,

d ~ 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
#include <iostream>
#include <algorithm>
#define MAX 1000
using namespace std;
typedef pair<intint> pi;
 
pi list[MAX];
bool visit[MAX + 1];
int N;
 
bool cmp(pi a, pi b) {
    if (a.second == b.second) return a.first < b.first;
    else return a.second > b.second;
}
 
void func() {
    int ans = 0;
    for (int i = 0; i < N; i++) {
        int d = list[i].first;
        for (int j = d; j > 0; j--) {
            if (visit[j]) continue;
            
            visit[j] = true;
            ans += list[i].second;
            break;
        }
    }
 
    cout << ans << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> list[i].first >> list[i].second;
    }
    sort(list, list + N, cmp);
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 16120 PPAP  (0) 2021.10.16
boj 2262 토너먼트 만들기  (0) 2021.10.16
boj 1826 연료 채우기  (0) 2021.02.22
boj 8980 택배  (0) 2021.02.16
boj 11000 강의실 배정  (0) 2021.02.16

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

 

1849번: 순열

1부터 N까지의 수들이 한 번씩 쓰인 수열이 있다. 그 수열에서 i 앞에 있는 수 들 중, i보다 큰 수들의 개수를 A[i]라고 정의하자. A[i]가 주어져 있을 때, 원래 수열을 구하는 프로그램을 작성하여라

www.acmicpc.net

사탕 상자 문제와 비슷한 방법인 구간합을 이용하여 해결하였습니다.

https://emoney96.tistory.com/93 (사탕 상자 문제풀이)

 

먼저 1 ~ N 만큼의 리프노드가 모두 1로 이루어진 세그먼트 트리를 구합니다.

입력으로는 1 ~ N 까지 자신보다 왼쪽에 있는 수 중에 자신보다 큰 수의 갯수가 주어집니다.

 

가장 작은 숫자인 1부터 입력으로 주어진 자신보다 큰 수의 갯수 + 1이 되는 자리를 찾습니다.

만약 cnt[1] = 5면 1의 자리는 6이 됩니다.

자리를 찾게 되면 해당 자리의 리프노드의 값을 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
58
#include <iostream>
#define MAX 100000
using namespace std;
 
int list[MAX + 1], tree[(MAX + 1* 4];
int cnt[MAX + 1];
int N;
 
int init(int node, int s, int e) {
    if (s == e) {
        return tree[node] = 1;
    }
 
    int m = (s + e) / 2;
    return tree[node] = init(node * 2, s, m) + init(node * 2 + 1, m + 1, e);
}
 
void query(int node, int s, int e, int idx, int k) {
    if (s == e) {
        tree[node] = 0;
        list[s] = idx;
        return;
    }
 
    int m = (s + e) / 2;
    if (k <= tree[node * 2]) query(node * 2, s, m, idx, k);
    else query(node * 2 + 1, m + 1, e, idx, k - tree[node * 2]);
 
    tree[node] = tree[node * 2+ tree[node * 2 + 1];
}
 
void func() {
    for (int i = 1; i <= N; i++) {
        query(11, N, i, cnt[i] + 1);
    }
 
    for (int i = 1; i <= N; i++) {
        cout << list[i] << '\n';
    }
}
 
void input() {
    cin >> N;
    init(11, N);
    for (int i = 1; i <= N; i++) {
        cin >> cnt[i];
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 16993 연속합과 쿼리  (0) 2022.08.29
boj 12846 무서운 아르바이트  (0) 2022.08.19
boj 14719 빗물  (0) 2021.03.15
boj 2304 창고 다각형  (0) 2021.02.25
boj 14438 수열과 쿼리 17  (0) 2021.02.21

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

 

17090번: 미로 탈출하기

크기가 N×M인 미로가 있고, 미로는 크기가 1×1인 칸으로 나누어져 있다. 미로의 각 칸에는 문자가 하나 적혀있는데, 적혀있는 문자에 따라서 다른 칸으로 이동할 수 있다. 어떤 칸(r, c)에 적힌 문

www.acmicpc.net

 

https://emoney96.tistory.com/24

 

boj 1103 게임

https://www.acmicpc.net/problem/1103 1103번: 게임 줄에 보드의 세로 크기 N과 가로 크기 M이 주어진다. 이 값은 모두 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에 보드의 상태가 주어진다. 쓰여

emoney96.tistory.com

dfs + dp 문제이며 위의 문제와 비슷한 방법으로 해결하였습니다.

 

대신 게임 문제는 방향이 정해져있지 않아서 4방향 모두 확인해야하지만 이 문제는 1방향만 확인하면 되는 문제입니다.

현재 좌표의 방향으로 이동하면서 맵 밖으로 나가면 1을 리턴, visit으로 사이클을 체크해서 사이클이 발생했으면 0을 리턴합니다.

 

N * M번 모두 돌려가면서 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
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 char list[][] = new char[510][510];
    static boolean visit[][] = new boolean[510][510];
    static int dp[][] = new int[510][510];
    static int direct[][] = { { 01 }, { 10 }, { 0-1 }, { -10 } };
    static int N, M, ans;
 
    static int dfs(int x, int y) {
        if (visit[x][y])
            return 0;
        visit[x][y] = true;
 
        if (dp[x][y] != -1)
            return dp[x][y];
 
        int d = 0;
        if (list[x][y] == 'D')
            d = 1;
        else if (list[x][y] == 'R')
            d = 0;
        else if (list[x][y] == 'U')
            d = 3;
        else
            d = 2;
 
        int nx = x + direct[d][0];
        int ny = y + direct[d][1];
 
        if (nx < 0 || nx >= N || ny < 0 || ny >= M)
            dp[x][y] = 1;
        else {
            dp[x][y] = dfs(x + direct[d][0], y + direct[d][1]);
            visit[nx][ny] = false;    
        }
 
        return dp[x][y];
    }
 
    static void func() {
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                if (dp[i][j] != -1) {
                    ans += dp[i][j];
                    continue;
                }
                ans += dfs(i, j);
                visit[i][j] = false;
            }
        }
 
        System.out.println(ans);
    }
 
    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++) {
            st = new StringTokenizer(br.readLine());
            list[i] = st.nextToken().toCharArray();
            Arrays.fill(dp[i], -1);
        }
 
    }
 
    public static void main(String[] args) throws Exception {
        input();
        func();
    }
}
cs

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

boj 20500 Ezreal 여눈부터 가네 ㅈㅈ  (0) 2022.01.01
boj 1135 뉴스 전하기  (0) 2021.11.15
boj 1648 격자판 채우기  (0) 2021.06.22
boj 14700 넴모넴모 (Hard)  (0) 2021.06.22
boj 1311 할 일 정하기 1  (0) 2021.06.21

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

 

20040번: 사이클 게임

사이클 게임은 두 명의 플레이어가 차례대로 돌아가며 진행하는 게임으로, 선 플레이어가 홀수 번째 차례를, 후 플레이어가 짝수 번째 차례를 진행한다. 게임 시작 시 0 부터 n − 1 까지 고유한

www.acmicpc.net

코테를 자바로 해야하기 때문에 오랜만에 자바로 리뷰를 합니다...

오랜만에 자바로 알고리즘 하니까 어색하네요

 

기본적인 union-find를 활용해볼 수 있는 문제입니다.

 

u와 v 노드를 연결 할 때 사이클이 생기는 경우는 u의 루트와 v의 루트가 같을 때입니다.

따라서 union-find 과정 중에 u의 루트와 v의 루트가 같은지 확인하여 같으면 true, 다르면 false를 리턴해주었고,

 

맨 처음 true를 받아온 경우에만 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
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 parent[] = new int[500001];
    static int N, M, ans;
 
    static int find(int v) {
        if (parent[v] == v)
            return v;
        return parent[v] = find(parent[v]);
    }
 
    static boolean union(int x, int y) {
        int a = find(x);
        int b = find(y);
 
        if (a == b)
            return true;
        parent[b] = a;
        return false;
    }
 
    static void init() {
        for (int i = 0; i < N; i++)
            parent[i] = i;
    }
 
    static void input() throws Exception {
        int u, v;
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        init();
        for (int i = 1; i <= M; i++) {
            st = new StringTokenizer(br.readLine());
            u = Integer.parseInt(st.nextToken());
            v = Integer.parseInt(st.nextToken());
 
            if (union(u, v) && ans == 0)
                ans = i;
        }
 
        System.out.println(ans);
    }
 
    public static void main(String[] args) throws Exception {
        input();
    }
}
cs

'algorithm > Union-Find' 카테고리의 다른 글

boj 1043 거짓말  (0) 2021.04.05
boj 10775 공항  (0) 2021.03.17
boj 4195 친구 네트워크  (0) 2021.03.17
boj 1976 여행 가자  (0) 2021.03.17
boj 1717 집합의 표현  (0) 2021.02.06

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

 

1648번: 격자판 채우기

준규는 침대에 누워서 천장을 바라보고 있었다. 천장은 격자판 모양이었고, 계속해서 천장을 바라보다 보니 이런 생각이 들었다. 세로 크기가 N이고, 가로 크기가 M인 격자판을 2x1 크기의 도미노

www.acmicpc.net

dp + 비트마스킹 문제로 넴모넴모(문제 링크) 문제와 비슷한 방식으로 해결하였습니다.

(안 푸셨으면 풀어 보시는거 추천드립니다 ㅎㅎ)

 

dp[x][bit] : x번 칸부터 M개의 칸의 상태가 bit일 때 x번 칸에 도미노를 놓는 경우의 수

이렇게 두고 해결하였습니다.

 

넴모넴모 문제는 x번 칸 이전의 M + 1개의 칸을 비트로, 이 문제는 x번 칸부터 M개의 칸을 비트로 두었습니다.

그리고 넴모넴모 문제는 x번 칸 뒤에는 채워져있지 않지만 이 문제는 채워져있을 가능성이 있어 확인을 해야하고,

0번 ~ x - 1번 칸은 모두 채워져있도록 로직을 구성하였습니다.

만약 2 * 3 격자판에 칸이 이렇게 채워져있고, x번 칸 차례라고 한다면,

x번 부터 x + (M - 1)번까지 M개의 비트는 역순으로 011로 구성됩니다.

 

우선 위 그림처럼 x번 칸이 이미 채워져있으면 다음 칸(x + 1)으로 넘어갑니다.

 

이렇게 x번 칸이 비워져있을 경우에만 1 * 2 크기나, 2 * 1 크기로 채울 수 있습니다.

위 그럼은 x + 1번 칸이 이미 채워져있으므로 1 * 2 크기는 채울 수 없습니다.

x + M번은 무조건 비어있으므로 2 * 1 크기로 채운 후 다음(x + 1)으로 넘어갈 수 있습니다.

 

이 그림은 x + 1번 칸이 비어있으므로 1 * 2크기를 채운 후 다음(x + 2)으로 넘어갈 수 있습니다.

x + M번은 무조건 비어있으므로 2 * 1 크기로 채운 후 다음(x + 1)으로 넘어갈 수 있습니다.

 

다만 이 그림처럼 맨 밑 칸은 2 * 1을 놓을 수 없으므로 맨 밑 칸이 아닐 경우에만 채워줍니다.

마지막으로 x번이 맨 오른쪽 칸일 경우에는 1 * 2크기로 채울 수 없으므로 맨 오른쪽 칸이 아닐 경우에만 채워줍니다.

 

 

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 <cstring>
#define MAX 14
#define MOD 9901
using namespace std;
 
int dp[MAX * MAX][1 << MAX];
int N, M;
 
int func(int x, int bit) {
    if (x == N * M) return 1;
 
    int &ret = dp[x][bit];
    if (ret != -1return ret;
    ret = 0;
 
    if (bit & 1) ret = func(x + 1, (bit >> 1));
    else {
        if (x / M != N - 1) ret = func(x + 1, (bit >> 1| (1 << (M - 1)));
 
        if (x%M != M - 1 && !(bit & 2)) ret = (ret + func(x + 2, bit >> 2)) % MOD;
    }
 
    return ret;
}
 
void input() {
    cin >> N >> M;
    memset(dp, -1sizeof(dp));
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    cout << func(00<< '\n';
 
    return 0;
}
cs

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

boj 1135 뉴스 전하기  (0) 2021.11.15
boj 17090 미로 탈출하기  (0) 2021.06.27
boj 14700 넴모넴모 (Hard)  (0) 2021.06.22
boj 1311 할 일 정하기 1  (0) 2021.06.21
boj 1577 도로의 개수  (0) 2021.06.20

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

 

14700번: 넴모넴모 (Hard)

첫 번째 줄에 주어진 격자판에서 나올 수 있는, “넴모”들이 올라간 칸이 2 × 2 사각형을 이루지 않는 모든 배치의 가짓수를 1, 000, 000, 007로 나눈 나머지를 출력한다.

www.acmicpc.net

dp + 비트마스킹 문제로 격자판 채우기(문제 링크) 문제와 비슷한 방식으로 해결하였습니다. 

(안 푸셨으면 풀어 보시는거 추천드립니다 ㅎㅎ)

 

dp[x][chk] : x번 칸에서 이전 M + 1개의 칸에 넴모가 채워져있는 상태가 chk인 경우의 수

이렇게 두고 해결하였습니다.

 

격자판 채우기 문제는 x번 칸부터 M개의 칸을 비트로 다루었고, 이 문제는 x번 칸 이전의 M+1개의 칸을 비트로 다루었습니다.

 

우선 맨 왼쪽 칸은 무조건 넴모를 놓을 수 있습니다.. 그 칸에 놓음으로 2 * 2 사각형을 만들 수 없기 때문입니다.

이부분도 생각해주셔야합니다. (x % M == 0 인 곳)

만약 2 * 3 격자판에 칸이 이렇게 채워져있고, x번 칸 차례라고 한다면,

M + 1개의 비트는 x - 1부터 해서 역순으로 1101이라고 생각하시면 됩니다.

그럼 x번 칸에서 봐야할 비트는 (1 << 0) , (1 << 1), (1 << M) 이렇게 3개입니다.

위의 그림에서는 (1 << 1) 비트가 0이므로 x에는 넴모를 올려놓은 것, 올려놓지 않은 것 모두 확인할 수 있습니다.

만약 이 그림이라면 3개의 비트 모두 1이므로 x에는 넴모를 올려놓지 않은 것만 확인하시면 됩니다.

 

 

그리고.. 이 문제가 메모리가 빠듯해서 그런지 1 << 19로 잡아도 메모리 초과가 발생하였습니다.

입력이 N * M <= 300으로 들어오기때문에 10 * 30 이런것도 들어올 수 있다는 것인데

비트는 M + 1개를 다루기 때문에 메모리 공간을 잡는데 어려움이 있었습니다.

17 * 17 = 289이고 18 * 18 = 324이므로 아무리 커도 N과 M 중 작은쪽은 17보다 클 수 없다는 생각을 하였고,

둘 중 작은쪽을 M으로 가게 하였습니다.

 

시간 제한이 2초인데 1.6초로 겨우 AC를 받았습니다..

 

 

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
#include <iostream>
#include <cstring>
#define MOD 1000000007
using namespace std;
 
int dp[301][1 << 18];
int N, M;
 
int func(int x, int chk) {
    if (x == N * M) return 1;
 
    int &ret = dp[x][chk];
    if (ret != -1return ret;
    ret = 0;
 
    ret = func(x + 1, chk >> 1);
 
    if (!(x % M) || !(chk & (1 << 0)) || !(chk & (1 << 1)) || !(chk & (1 << M))) {
        ret = (ret + func(x + 1, (chk >> 1| (1 << M))) % MOD;
    }
 
    return ret;
}
 
void input() {
    cin >> N >> M;
    if (N < M) swap(N, M);
    memset(dp, -1sizeof(dp));
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    cout << func(00<< '\n';
 
    return 0;
}
cs

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

boj 17090 미로 탈출하기  (0) 2021.06.27
boj 1648 격자판 채우기  (0) 2021.06.22
boj 1311 할 일 정하기 1  (0) 2021.06.21
boj 1577 도로의 개수  (0) 2021.06.20
boj 4781 사탕 가게  (0) 2021.06.19

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

 

1311번: 할 일 정하기 1

N명의 사람과 N개의 일이 있다. 각 사람은 일을 하나 담당해야 하고, 각 일을 담당하는 사람은 한 명 이어야 한다. 또한, 모든 사람은 모든 일을 할 능력이 있다. 사람은 1번부터 N번까지 번호가 매

www.acmicpc.net

외판원 문제를 풀때는 dp + 비트마스킹에 적응을 못했어서 푸는데 어려움이 많았지만 이번 문제는 좀 수월하게 했던것 같습니다..

 

dp[x][cost] : x번 사람이 고를 때 x - 1번째 사람까지 골랐던 일의 상태가 cost인 비용의 최솟값

여기서 cost에 비트마스킹을 이용합니다. (N = 20이므로 약 100만 정도의 크기입니다.)

 

저는 번호를 0 ~ N - 1로 두었기때문에 0번 사람부터 차례로 0 ~ N - 1번 일까지 돌면서 고르지 않은 일만 선택하였고,

x가 N일 때 모든 사람이 일을 골랐으면 0, 아니면 INF을 리턴하였습니다.

 

 

+ 음.. 시간과 메모리가 적게 나온 분의 풀이를 보았는데 dp[x][cost]가 아닌 dp[cost]만으로도 해결이 됐습니다..

dp[cost] : 고른 일의 상태가 cost일 때 비용의 최솟값

으로 두고 해결하면 될 것 같습니다! (소스는 같이 올리겠습니다)

 

역시 1차원 배열로 한 것이 시간적으로나 메모리적으로나 많은 이득을 가져갑니다..

 

 

[2차원 배열]

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 <cstring>
#include <algorithm>
#define MAX 20
#define INF 1000000000
using namespace std;
 
int list[MAX][MAX], dp[MAX][1 << MAX];
int N, chk;
 
int func(int x, int cost) {
    if (x == N) {
        if(cost == chk) return 0;
        else return INF;
    }
 
    int &ret = dp[x][cost];
    if (ret != -1return ret;
    ret = INF;
 
    for (int i = 0; i < N; i++) {
        if (cost & (1 << i)) continue;
 
        ret = min(ret, func(x + 1, cost | (1 << i)) + list[x][i]);
    }
 
    return ret;
}
 
void input() {
    cin >> N;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> list[i][j];
        }
    }
    memset(dp, -1sizeof(dp));
    chk = (1 << N) - 1;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    cout << func(00<< '\n';
 
    return 0;
}
cs

 

 

[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
#include <iostream>
#include <cstring>
#include <algorithm>
#define MAX 20
#define INF 1000000000
using namespace std;
 
int list[MAX][MAX], dp[1 << MAX];
int N, chk;
 
int func(int x, int cost) {
    if (x == N) {
        if(cost == chk) return 0;
        else return INF;
    }
 
    int &ret = dp[cost];
    if (ret != -1return ret;
    ret = INF;
 
    for (int i = 0; i < N; i++) {
        if (cost & (1 << i)) continue;
 
        ret = min(ret, func(x + 1, cost | (1 << i)) + list[x][i]);
    }
 
    return ret;
}
 
void input() {
    cin >> N;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            cin >> list[i][j];
        }
    }
    memset(dp, -1sizeof(dp));
    chk = (1 << N) - 1;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    cout << func(00<< '\n';
 
    return 0;
}
cs

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

boj 1648 격자판 채우기  (0) 2021.06.22
boj 14700 넴모넴모 (Hard)  (0) 2021.06.22
boj 1577 도로의 개수  (0) 2021.06.20
boj 4781 사탕 가게  (0) 2021.06.19
boj 2186 문자판  (0) 2021.06.19

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

 

1577번: 도로의 개수

첫째 줄에 도로의 가로 크기 N과 세로 크기 M이 주어진다. N과 M은 100보다 작거나 같은 자연수이고, 둘째 줄에는 공사중인 도로의 개수 K가 주어진다. K는 0보다 크거나 같고, 100보다 작거나 같은

www.acmicpc.net

(0, 0)에서 출발하여 (N, M)에 도착할 수 있는 경우의 수를 구하는문제입니다.

다만 이 문제는 공사중이어서 갈 수 없는 길이 존재하는데 거리는 항상 1이므로 set으로 관리하였습니다.

 

(a, b), (c, d) 형식으로 입력이 주어지면 (a, b)에서 (c, d)로 가는 길, (c, d)에서 (a, b)로 가는 길 모두 체크하였습니다.

로직은 dfs+dp으로 구성하였고, 이동은 최단거리로만 이동하기 때문에 뒤로는 가지 않아야합니다.

 

이 문제의 답은 long long 범위라고 명시되어 있으므로 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
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
typedef long long ll;
 
set<pair<intint> > s[101][101];
ll dp[101][101];
int list[101][101];
int direct[2][2= { {0,1},{1,0} };
int N, M, K;
 
ll func(int x, int y) {
    if (x == N && y == M) return 1;
 
    ll &ret = dp[x][y];
    if (ret != -1return ret;
    ret = 0;
 
    for (int i = 0; i < 2; i++) {
        int nx = x + direct[i][0];
        int ny = y + direct[i][1];
 
        if (nx > N || ny > M) continue;
        if (s[x][y].find({ nx,ny }) != s[x][y].end()) continue;
 
        ret += func(nx, ny);
    }
 
    return ret;
}
 
void input() {
    int sx, sy, ex, ey;
    cin >> N >> M >> K;
    while (K--) {
        cin >> sx >> sy >> ex >> ey;
        s[sx][sy].insert({ ex,ey });
        s[ex][ey].insert({ sx,sy });
    }
    memset(dp, -1sizeof(dp));
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    cout << func(00<< '\n';
    
    return 0;
}
cs

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

boj 14700 넴모넴모 (Hard)  (0) 2021.06.22
boj 1311 할 일 정하기 1  (0) 2021.06.21
boj 4781 사탕 가게  (0) 2021.06.19
boj 2186 문자판  (0) 2021.06.19
boj 13325 이진 트리  (0) 2021.06.18

+ Recent posts