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

 

N개의 정점으로 이루어진 트리에서 i < j인 정점들 사이 거리가 최소가 되도록 하려면 어떤 트리를 만들어야하는지 구하는 문제입니다.

 

트리를 직접 그려보면 Star Graph 형식이 최소가 된다는 것을 알 수 있습니다.

Star Graph는 하나의 정점에 다른 모든 정점이 연결된 형태입니다.

 

이렇게되면 1번 정점과 연결된 다른 정점은 거리가 각각 1입니다. (N - 1)

나머지 정점들끼리의 거리는 각각 2입니다.

나머지 N - 1개의 정점에서 2개를 고르는 경우의 수는 N-1C2이며, 두개 곱하면 (N - 2) * (N - 1)이 됩니다.

위에서 구한 두 수를 더하면 N - 1 + (N - 2) * (N - 1) 이라는 답을 구할 수 있습니다.

 

스페셜 저지 문제이기 때문에 기준이 되는 정점을 아무거나 고르시면 됩니다. 저는 1번 정점을 기준으로 정했습니다.

그리고 기준이 되는 정점과 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
#include <iostream>
using namespace std;
typedef long long ll;
 
ll N;
 
void func() {
    cout << N - 1LL + (N - 2LL) * (N - 1LL) << '\n';
    for (int i = 2; i <= N; i++) {
        cout << "1 " << i << '\n';
    }
}
 
void input() {
    cin >> N;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

 

순열 그래프는 N개의 정점이 1 ~ N의 번호가 하나씩 구성되어 있습니다.

그리고 두 정점 쌍 i, j (1 ≤ i < j ≤ n) 는 ai > aj 일때 간선으로 연결되어 있습니다.

4
2 3 1 4

순열이 이렇게 주어졌을 때  2, 3 > 1이므로 (1, 2), (1, 3)이 각각 연결되어 있고

(1, 2, 3) (4) 로 집합을 나눌 수 있습니다.

 

여기서 알 수 있는건 i번째 까지 순회했을때 max가 i이면 해당 위치까지 하나의 집합으로 묶을 수 있습니다.

위의 예시로 보면

3번 인덱스까지의 max가 3이므로 1, 2, 3을 집합으로 묶을 수 있고, 4번 인덱스까지의 max가 4이므로 4를 집합으로 묶을 수 있습니다.

이전 집합의 max값을 유지해주면서 배열을 순회하면 정답을 구할 수 있습니다.

 

 

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 <vector>
#include <algorithm>
#define MAX 1000001
using namespace std;
 
vector<int> v;
int list[MAX];
int N;
 
void func() {
    int maxValue = 0;
    for (int i = 1; i <= N; i++) {
        maxValue = max(maxValue, list[i]);
        if (i == maxValue) v.push_back(i);
    }
 
    int pre = 0;
    cout << v.size() << '\n';
    for (auto x : v) {
        cout << x - pre << ' ';
        for (int i = pre + 1; i <= x; i++cout << i << ' ';
        cout << '\n';
        pre = x;
    }
}
 
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 > ad-hoc' 카테고리의 다른 글

boj 27966 △  (0) 2024.08.22
boj 16161 가장 긴 증가하는 팰린드롬 부분수열  (0) 2024.08.15
boj 24553 팰린드롬 게임  (0) 2024.08.07
boj 14370 전화번호 수수께끼 (Large)  (0) 2024.08.06
boj 21316 스피카  (0) 2024.08.05

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

 

팰린드롬은 앞/뒤에서 읽었을 때 같은 수열입니다.

이 문제에서는 증가하는 팰린드롬을 구해야 합니다.

 

1, 2, 3, 4, 3, 2, 1
1, 3, 6, 7, 7, 6, 3, 1

이런것처럼 중앙으로 갈수록 값이 커지는 것이 증가하는 팰린드롬 수열입니다.

 

1, 2, 4, 3, 2, 1
4, 3, 2, 3, 4
1, 1, 1, 1

 

 

증가하는 팰린드롬이 아닌 경우는

1. 애초에 팰린드롬이 아닌 경우

2. 팰린드롬이지만 값이 증가하지 않는 경우 (값이 같아도 x)

이렇게 두가지가 되겠습니다.

 

저는 중앙을 먼저 정해놓고 한칸씩 이동하여 팰린드롬의 길이를 직접 구해봤습니다.

팰린드롬의 길이에 따라 중앙은 1개 or 2개가 될 수 있습니다.

따라서 l번째 수와 l + 1번째 수가 같으면 묶어서 중앙으로 두고 구할 수 있습니다.

이후에는 그냥 왼쪽, 오른쪽으로 한칸씩 이동하면서 증가하는 팰린드롬인지 확인해주시고, 최대 길이를 갱신해주시면 됩니다.

 

그리고 이 문제가 "증가하는 팰린드롬"이기 때문에 하나 더 생각할 수 있는건

s ~ e 범위의 팰린드롬을 찾았다고 했을 때, list[s], list[e]은 해당 팰린드롬에서 항상 가장 작은 수를 담당하고 있습니다.

그렇기 때문에 e번째 수에서 왼쪽으로 이동하더라도 큰 수가 있을 수밖에 없고, 더 큰 범위의 팰린드롬을 찾을 수 없습니다.

따라서 중앙값인 l, r을 +1씩 볼 필요 없이 s ~ e 범위의 다음 인덱스부터 다시 확인해주시면 불필요한 계산 없이 정답을 구할 수 있습니다.

 

 

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
#include <iostream>
#include <algorithm>
#define MAX 100001
using namespace std;
 
int list[MAX];
int N;
 
void func() {
    int l = 0;
    int ret = 1;
    while (l < N) {
        int r = l;
        if (r + 1 < N && list[l] == list[r + 1]) r++;
 
        int s = l - 1;
        int e = r + 1;
        while (s >= 0 && e < N) {
            if (list[s] != list[e] || list[s] >= list[s + 1]) break;
            e++;
            s--;
        }
 
        ret = max(ret, e - s - 1);
        l = e;
    }
    cout << ret << '\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 > ad-hoc' 카테고리의 다른 글

boj 27966 △  (0) 2024.08.22
boj 7982 순열 그래프의 연결성 판별  (0) 2024.08.16
boj 24553 팰린드롬 게임  (0) 2024.08.07
boj 14370 전화번호 수수께끼 (Large)  (0) 2024.08.06
boj 21316 스피카  (0) 2024.08.05

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

 

오븐의 깊이에 따라 피자가 들어갈 수 있는 크기가 다릅니다.

오븐에 피자를 위에서부터 차례대로 쌓는 방식인데 중간에 크기가 좁은 오븐을 만나면 더이상 내려갈 수 없습니다.

따라서 오븐의 크기를 먼저 조정해줄 수 있습니다.

 

5 6 4 3 6 2 3

오븐의 크기가 위에서부터 이렇게 주어진다면

위에서부터 최솟값을 갱신한다면

 

5 5 4 3 3 2 2

이렇게 바뀌게되고, 크기가 같거나 작아지는 수열이 됩니다.

그러면 위에서부터 크기가 맞는 높이를 계속 찾아줄 필요 없이 밑에서부터 맞는 크기를 찾아주면 됩니다.

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
#include <iostream>
#include <algorithm>
#define MAX 300001
using namespace std;
 
int width[MAX];
int list[MAX];
int N, M;
 
void func() {
    int idx = N;
    for (int i = 0; i < M; i++) {
        bool flag = false;
        while (idx > 0) {
            if (width[idx] >= list[i]) {
                idx--;
                flag = true;
                break;
            }
            idx--;
        }
        if (!flag) {
            cout << "0\n";
            return;
        }
    }
    cout << idx + 1 << '\n';
}
 
void input() {
    cin >> N >> M;
    for (int i = 1; i <= N; i++) {
        cin >> width[i];
        if (i > 1) {
            width[i] = min(width[i], width[i - 1]);
        }
    }
 
    for (int i = 0; i < M; i++) {
        cin >> list[i];
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 3758 KCPC  (0) 2024.06.09
boj 17143 낚시왕  (0) 2021.04.23
boj 20207 달력  (0) 2021.04.22
boj 17144 미세먼지 안녕!  (0) 2021.04.14
boj 2564 경비원  (0) 2021.04.13

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

 

N개의 돌 중에 팰린드롬 수만큼의 돌을 가져갈 수 있으며, 마지막에 모든 돌을 가져가는 사람이 이깁니다.

 

우선 1 ~ 9는 팰린드롬이므로 무조건 먼저하는 사람이 이깁니다.

N = 10이라면 1 ~ 9개를 가져가더라도 9 ~ 1개는 무조건 남기 때문에 먼저하는 사람이 집니다.

그리고 N = 11 ~ 19는 1 ~ 9를 적절하게 뺀다면 다음 사람에게 10을 넘겨줄 수 있으므로 무조건 이길 수 있습니다.

이런식으로 간다면 10의 배수가 아닐때는 무조건 선공이 이긴다는 것을 알 수 있습니다.

0으로 시작하는 수는 없으므로 10의 배수는 팰린드롬이 올 수 없으므로 다른건 고려하지 않아도 됩니다.

 

 

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
#include <iostream>
using namespace std;
typedef long long ll;
 
ll N;
 
void func() {
    if (N % 10cout << "0\n";
    else cout << "1\n";
}
 
void input() {
    cin >> 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

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

 

안그래도 문자열에 약한데 애드혹까지 섞이니 뇌정지가 오는 문제였습니다.

우선 문자열이 섞여있는 채로 저장되어있고,  문자열이 어떤 수들로 구성되어 있는지 파악해야 합니다.

그러면 각 숫자들의 고유한 알파벳을 구해서 카운팅하는 방법을 떠올릴 수 있습니다.

 

0 2 4 6 8은 각각 고유한 알파벳이 있습니다.

0 - Z

2 - W

4 - U

6 - X

8 - G

 

그리고 이들을 제외한 숫자들 중 고유한 알파벳을 뽑으면

3 - H

5 - F

7 - S

9 - I

1 - O

순서대로 이렇게 가져올 수 있습니다.

 

먼저 입력으로 들어온 문자열의 문자들을 모두 카운팅합니다.

이후 위에서 구한 알파벳을 순서대로 가져와서 카운팅을 빼주면서 정답을 구하는 방식으로 구현했습니다.

 

 

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
#include <iostream>
#include <string>
#include <cstring>
#define MAX 10
using namespace std;
 
string str;
string alpha[MAX] = { "ZERO""ONE""TWO""THREE""FOUR""FIVE""SIX""SEVEN""EIGHT""NINE" };
pair<intchar> ch[MAX] = { {0,'Z'}, {2,'W'}, {4'U'}, {6'X'}, {8'G'}, {3,'H'}, {5'F'}, {7'S'}, {9'I'}, {1,'O'} };
int alphaCnt[26];
int cnt[MAX];
int len;
 
void solve() {
    for (auto c : ch) {
        while (alphaCnt[c.second - 'A']) {
            cnt[c.first]++;
            for (auto a : alpha[c.first]) {
                alphaCnt[a - 'A']--;
            }
        }
    }
}
 
void func(int tc) {
    for (auto x : str) {
        alphaCnt[x - 'A']++;
    }
    solve();
 
    cout << "Case #" << tc << ": ";
    for (int i = 0; i < MAX; i++) {
        while (cnt[i]--) {
            cout << i;
        }
    }
    cout << '\n';
}
 
void input() {
    cin >> str;
    len = str.size();
}
 
void init() {
    memset(cnt, 0sizeof(cnt));
    memset(alphaCnt, 0sizeof(alphaCnt));
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    int tc;
    cin >> tc;
    for (int t = 1; t <= tc; t++) {
        input();
        func(t);
        init();
    }
 
    return 0;
}
cs

'algorithm > ad-hoc' 카테고리의 다른 글

boj 16161 가장 긴 증가하는 팰린드롬 부분수열  (0) 2024.08.15
boj 24553 팰린드롬 게임  (0) 2024.08.07
boj 21316 스피카  (0) 2024.08.05
boj 10350 Banks  (0) 2024.07.31
boj 5527 전구 장식  (0) 2024.07.30

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

 

12개 간선이 입력으로 주어지고, 어떤 입력이든 이 모양으로 주어진다고 합니다.

여기서 Spica가 되는 정점의 조건은 주변 정점들과 간선들로 알아낼 수 있습니다.

 

저는 Spica에 인접한 정점들은 각각 1, 2, 3개의 간선이 있다는 것을 파악했고, 그대로 구현했습니다.

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
#include <iostream>
#include <vector>
#define MAX 13
using namespace std;
 
vector<int> v[MAX];
 
void func() {
    for (int i = 1; i < MAX; i++) {
        int flag = 0;
        for (auto next : v[i]) {
            flag |= (1 << (v[next].size() - 1));
        }
 
        if (flag == 7) {
            cout << i << '\n';
            return;
        }
    }
}
 
void input() {
    int x, y;
    for (int i = 1; i < MAX; i++) {
        cin >> x >> y;
        v[x].push_back(y);
        v[y].push_back(x);
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > ad-hoc' 카테고리의 다른 글

boj 24553 팰린드롬 게임  (0) 2024.08.07
boj 14370 전화번호 수수께끼 (Large)  (0) 2024.08.06
boj 10350 Banks  (0) 2024.07.31
boj 5527 전구 장식  (0) 2024.07.30
boj 14864 줄서기  (0) 2024.07.26

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

 

처음 주어지는 배열과 다르게 배치된 쌍의 갯수를 구하는 문제로 inversion counting 방식으로 접근할 수 있습니다.

inversion counting이란 순서가 바뀐 순서쌍을 구하는데 사용되는 기법입니다.

즉 자신보다 작지만 뒤에 몇 명이 서있는지 구한다거나 그런 문제들을 이 방식을 사용할 수 있습니다.

 

우선 입력이 문자열로 되어있으니 map을 활용하여 첫 번째 배열을 입력받는 순서대로 정수 인덱스로 변경해줍니다.

그 다음 두 번째 배열은 첫 번째 배열에서 구한 인덱스 번호로 변경합니다.

두 번째 입력에서 구한 정수 배열로 트리를 만들도록 합니다.

이 때 트리에 수를 넣어주면서 자신보다 크지만 먼저 들어온 수들의 갯수를 세어주면 됩니다.

배열이 2 3 1 순서대로 저장한다고 가정하면 1을 트리에 넣을때 이미 들어와있던 2, 3을 카운팅 해주는 것입니다.

 

일반적인 map을 사용한 코드의 채점현황입니다.

unordered_map으로 변경한 코드의 채점현황입니다.

이 문제처럼 순서가 중요하지 않고 조회가 많이 발생하는 경우 unordered_map을 사용하는게 더 효율적이라는 것을 알 수 있었습니다.

 

 

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
#include <iostream>
#include <unordered_map>
#include <string>
#include <cstring>
#define MAX 100001
using namespace std;
typedef long long ll;
 
unordered_map<stringint> m;
int idxList[MAX];
ll tree[MAX << 2];
int N;
 
void init() {
    m.clear();
    memset(tree, 0sizeof(tree));
}
 
ll update(int node, int l, int r, int idx, int diff) {
    if (idx < l || r < idx) return tree[node];
    if (l == r) return ++tree[node];
 
    int m = (l + r) >> 1;
    return tree[node] = update(node << 1, l, m, idx, diff) + update((node << 1+ 1, m + 1, r, idx, diff);
}
 
ll query(int node, int l, int r, int s, int e) {
    if (l > e || r < s) return 0LL;
    if (s <= l && r <= e) return tree[node];
 
    int m = (l + r) >> 1;
    return query(node << 1, l, m, s, e) + query((node << 1+ 1, m + 1, r, s, e);
}
 
void func() {
    ll ret = 0LL;
    for (int i = 0; i < N; i++) {
        ret += query(11, N, idxList[i] + 1, N);
        update(11, N, idxList[i], 1);
    }
    cout << ret << '\n';
}
 
void input() {
    cin >> N;
    if (!N) exit(0);
    
    string str;
    int cnt = 0;
    for (int i = 0; i < N; i++) {
        cin >> str;
        m[str] = ++cnt;
    }
 
    for (int i = 0; i < N; i++) {
        cin >> str;
        idxList[i] = m[str];
    }
}
 
int main() {
    cin.tie(nullptr); cout.tie(nullptr);
    ios::sync_with_stdio(false);
 
    while (1) {
        input();
        func();
        init();
    }
 
    return 0;
}
cs

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

boj 8330 순열  (0) 2024.07.21
boj 1517 버블 소트  (0) 2024.06.16
boj 17131 여우가 정보섬에 올라온 이유  (3) 2024.04.25
boj 12895 화려한 마을  (0) 2022.09.07
boj 10167 금광  (0) 2022.09.02

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

 

생애 첫 루비문제입니다.

애드혹 문제라서 그런지 백준 게시판이나 블로그에서 아이디어를 얻지 않았다면 절대 손도 못댈거같은 그런 문제였습니다..

 

제가 힌트를 얻은건 1 ~ N번째 수를 계속 이어붙였을 때 음수 누적합이 나오는 갯수를 구하는 것이었습니다.

사실상 문제의 모든것 이었습니다..

 

아무튼 저는 누적합으로 접근했고, 각 은행들은 원형으로 배치되어 있기때문에 1 ~ N이 아닌 1 ~ 2N의 누적합으로 구해줍니다.

그리고 1, 2, 3, ... N에서 시작하여 N개의 수를 계속 이어붙일 때의 누적합들 중에 음수가 몇번 나오는지 카운팅하면 그게 답입니다.

 

4
1 -2 -1 3

예시로 설명드리면

처음 1번부터 누적합을 쭉 구하면

1 -1 -2 1 / 2 0 -1 2 / 3 1 0 3

이렇게 되고, 음수의 갯수는 3개입니다.

그리고 이후에 배열을 계속 이어도 음수가 나오지 않습니다.

 

그 다음 2번부터 누적합을 구하면

x -2 -3 0 1 / -1 -2 1 2 / 0 -1 2 3

이렇게 되고, 음수의 갯수는 5개입니다.

 

다음 3번부터 누적합을 구하면

x x -1 2 3 1 / 0 3 4 2 / 1 4 5 3

이렇게 되고, 음수의 갯수는 1개입니다.

 

마지막 4번부터 누적합을 구하면

x x x 3 4 2 1 / 4 5 3 2 / 5 6 4 3

이렇게 되고, 음수의 갯수는 0개입니다.

위에서 구한 갯수들을 전부 더하면 9개가 됩니다.

 

이 과정에서 규칙을 하나 찾을 수 있습니다.

우선 문제에서 입력으로 주어지는 수를 모두 더하면 양수라고 했습니다.

그리고 각 시작 지점을 i번이라고 했을 때 i번부터 N개의 누적합은 무조건 양수입니다.

 

제가 4개씩 끊은 구간들을 보시면 1번 구간의 수들에서 +1한 값들이 2번 구간이 되어 있습니다.

그리고 2번 구간에서 +1를 하면 3번 구간이 됩니다.

그 +1은 배열의 전체 누적합이 되고, 음수의 갯수는 음수 누적합 / 전체 누적합이 되는것을 알 수 있습니다.

계산하면 실수가 나오기 때문에 double형으로 바꿔주셔야 하고, 나머지는 무조건 올림 연산을 해야합니다.

이 방식으로 접근하시면 로직 자체는 간단하게 구현하실 수 있습니다.

이 문제는 이 방식을 생각해내는것 자체가 정말 어렵다는 것을 느꼈습니다.

 

 

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
#include <iostream>
#include <cmath>
#define MAX 20001
using namespace std;
typedef long long ll;
 
int dp[MAX];
int N;
 
void init() {
    for (int i = 1; i <= (N << 1); i++) {
        dp[i] += dp[i - 1];
    }
}
 
void func() {
    init();
 
    ll ret = 0;
    for (int i = 1; i <= N; i++) {
        for (int j = i; j < N + i; j++) {
            if (dp[j] - dp[i - 1>= 0continue;
            ret += (ll)ceil((double)(-dp[j] + dp[i - 1]) / (double)(dp[N + i - 1- dp[i - 1]));
        }
    }
    cout << ret << '\n';
}
 
void input() {
    cin >> N;
    for (int i = 1; i <= N; i++) {
        cin >> dp[i];
        dp[i + N] = dp[i];
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

'algorithm > ad-hoc' 카테고리의 다른 글

boj 14370 전화번호 수수께끼 (Large)  (0) 2024.08.06
boj 21316 스피카  (0) 2024.08.05
boj 5527 전구 장식  (0) 2024.07.30
boj 14864 줄서기  (0) 2024.07.26
boj 2381 최대 거리  (0) 2024.07.25

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

 

특정 범위의 전구 상태를 뒤집는 기계를 1번만 사용하여 연속된 교대 패턴의 최대 크기를 구하는 문제입니다.

교대 패턴은 말 그대로 인접한 전구의 상태가 다른 패턴입니다. 1 0 1 0 1 이나 0 1 0 1같은 패턴입니다.

 

dp를 활용하여 특정 범위를 뒤집으면서 할 수도 있겠지만 다른 방법으로 쉽게 풀렸습니다.

1 1 0 0 1 0 1 1 1 0

입력이 이렇게 주어졌을때 각 교대패턴의 크기를 구하면

1 2 4 1 2 이렇게 됩니다.

그렇다면 여기서 한 구간을 뒤집게 되면 왼쪽과 오른쪽에 인접한 구간과 하나의 패턴으로 합쳐질 수 있습니다.

 

각 교대 패턴이 1 0 / 0 1 0 1 / 1

이렇게 되어있다고 했을때 0 1 0 1을 뒤집어주면 1 0 1 0이 됩니다.

그러면 전구들의 상태는 1 0 / 1 0 1 0 / 1 이렇게 되므로 세 구간은 하나로 합쳐지고, 크기는 7이 됩니다.

즉 연속된 3개 패턴 크기의 합의 최댓값을 구해주시면 답을 구하실 수 있습니다.

물론 패턴의 갯수가 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
53
54
55
56
57
58
#include <iostream>
#include <vector>
#define MAX 100001
using namespace std;
 
vector<int> v;
int list[MAX];
int N;
 
void init() {
    int pre = list[0];
    int cnt = 1;
    for (int i = 1; i < N; i++) {
        if (pre == list[i]) {
            v.push_back(cnt);
            cnt = 1;
        }
        else {
            cnt++;
        }
        pre = list[i];
    }
    v.push_back(cnt);
}
 
void func() {
    init();
 
    int ret = 0;
    if (v.size() < 3) {
        for (auto x : v) {
            ret += x;
        }
    }
    else {
        for (int i = 1; i < v.size() - 1; i++) {
            ret = max(ret, v[i - 1+ v[i] + v[i + 1]);
        }
    }
    cout << ret << '\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 > ad-hoc' 카테고리의 다른 글

boj 21316 스피카  (0) 2024.08.05
boj 10350 Banks  (0) 2024.07.31
boj 14864 줄서기  (0) 2024.07.26
boj 2381 최대 거리  (0) 2024.07.25
boj 14675 단절점과 단절선  (0) 2024.07.23

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

 

회의는 한번에 하나만 진행이 가능하며, 주어지는 N개의 회의들을 적절하게 배정하여 최대 인원을 구하는 문제입니다.

저는 dp + parametric search로 접근했습니다.

dp[N]: N번째 회의부터 모을 수 있는 최대 인원

 

우선 회의들을 시작시간 기준 오름차순으로 정렬합니다.

그리고 해당 회의를 진행하거나, 진행하지 않거나 두가지를 모두 확인할 수 있습니다.

 

이번 회의를 넘긴다면 바로 idx + 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
#include <iostream>
#include <algorithm>
#include <cstring>
#define MAX 100001
using namespace std;
 
typedef struct Node {
    int l, r, s;
}Node;
 
Node list[MAX];
int dp[MAX];
int N;
 
bool cmp(Node a, Node b) {
    return a.l < b.l;
}
 
int bs(int x) {
    int l = 0;
    int r = N;
    while (l < r) {
        int m = (l + r) >> 1;
        if (list[m].l >= x) r = m;
        else l = m + 1;
    }
    return l;
}
 
int solve(int idx) {
    if (idx >= N) return 0;
 
    int& ret = dp[idx];
    if (ret != -1return ret;
 
    return ret = max(solve(idx + 1), list[idx].s + solve(bs(list[idx].r)));
}
 
void func() {
    sort(list, list + N, cmp);
    memset(dp, -1sizeof(dp));
    cout << solve(0<< '\n';
}
 
void input() {
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> list[i].l >> list[i].r >> list[i].s;
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 30461 낚시  (0) 2024.07.28
boj 17258 인기가 넘쳐흘러  (0) 2024.07.20
boj 1398 동전 문제  (0) 2024.07.04
boj 1229 육각수  (0) 2024.06.30
boj 25378 조약돌  (0) 2024.06.29

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

 

점화식만 구한다면 쉽게 풀리는 문제입니다.

2차원 배열의 누적합이지만 기존 방식처럼 (1, 1) ~ (x, y)의 모든 값을 더하는 것이 아닌 대각선 범위에 있는 수들을 더해야합니다.

 

우선 본인보다 위에 있는 수는 포함되어야 합니다. 그러면 dp[i - 1][j]를 추가합니다.

다음은 본인보다 대각선 위에 있는 수도 포함되어야 합니다. 그러면 dp[i - 1][j - 1]가 추가됩니다.

 

여기서 하나 생각해야할건 dp[i - 1][j]에는 dp[i - 2][j - 1]이 포함되어 있습니다. (대각선)

그리고 dp[i - 1][j - 1]에도 dp[i - 2][j - 1]가 포함되어 있습니다. (위)

그러니 i > 1인 좌표에서는 dp[i - 2][j - 1]를 한번 빼줘야 점화식이 완성됩니다.

 

그러면 최종 점화식은

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

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

boj 19623 회의실 배정 4  (0) 2024.07.29
boj 17258 인기가 넘쳐흘러  (0) 2024.07.20
boj 1398 동전 문제  (0) 2024.07.04
boj 1229 육각수  (0) 2024.06.30
boj 25378 조약돌  (0) 2024.06.29

+ Recent posts