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

 

순서쌍 (x, y)는 학생y 가 학생x 보다 뒤에 있으면서 더 작은 번호를 가지고 있다는 것을 의미하며,

입력으로 주어지는 순서쌍을 제외하면 더 작은 번호를 갖고있는 학생은 무조건 앞에 있다는 뜻이 됩니다.

 

그러면 최초 입력을 받기 전에는 1 ~ N번째 학생은 1 ~ N번을 순서대로 들고있게 됩니다.

그리고 입력되는 (x, y)에서 x는 본인보다 뒤에 있는 y보다 큰 수를 갖고 있으므로 +1으로 카운팅할 수 있습니다.

반대로 y는 -1으로 카운팅할 수 있습니다.

 

그렇게 수정된 배열에는 학생들이 들고있는 번호가 완성되지만 중복이 포함될 수 있습니다.

그런 경우에는 -1을 출력해주시고, 그렇지 않다면 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
#include <iostream>
#define MAX 100001
using namespace std;
 
int list[MAX];
bool chk[MAX];
int N, M;
 
void init() {
    for (int i = 1; i <= N; i++) {
        list[i] = i;
    }
}
 
void func() {
    for (int i = 1; i <= N; i++) {
        if (chk[list[i]]) {
            cout << "-1\n";
            return;
        }
        chk[list[i]] = true;
    }
 
    for (int i = 1; i <= N; i++) {
        cout << list[i] << ' ';
    }
    cout << '\n';
}
 
void input() {
    int x, y;
    cin >> N >> M;
    init();
    while (M--) {
        cin >> x >> y;
        list[x]++;
        list[y]--;
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 10350 Banks  (0) 2024.07.31
boj 5527 전구 장식  (0) 2024.07.30
boj 2381 최대 거리  (0) 2024.07.25
boj 14675 단절점과 단절선  (0) 2024.07.23
boj 27468 2배 또는 0.5배  (0) 2024.07.22

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

 

두 점의 좌표 (a, b), (c, d)가 있을 때 L1-metric 거리는 |a - c| + |b - d| 이라고 하고 L1-metric의 최댓값을 구하는 문제입니다.

우선 절댓값부터 전개를 하면

1. (a + b) - (c + d)

2. - (a + b) + (c + d)

3. (a - b) - (c - d)

4. - (a - b) + (c - d)

이렇게 되는것을 알 수 있습니다.

 

여기서 (1, 2), (3, 4)를 묶어서 보면 각각 x + y, x - y의 차이를 계산하고 있습니다.

그렇다면 x + y, x - y를 배열에 각각 넣고 정렬해서 최대 - 최소를 구한다면 답을 구할 수 있습니다.

이 때 x + y의 최대 - 최소, x - y의 최대 - 최소 중 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
#include <iostream>
#include <algorithm>
#define MAX 50001
using namespace std;
 
int list[2][MAX];
int N;
 
void func() {
    sort(list[0], list[0+ N);
    sort(list[1], list[1+ N);
    cout << max(list[0][N - 1- list[0][0], list[1][N - 1- list[1][0]) << '\n';
}
 
void input() {
    int x, y;
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> x >> y;
        list[0][i] = x + y;
        list[1][i] = x - y;
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 5527 전구 장식  (0) 2024.07.30
boj 14864 줄서기  (0) 2024.07.26
boj 14675 단절점과 단절선  (0) 2024.07.23
boj 27468 2배 또는 0.5배  (0) 2024.07.22
boj 1377 버블 소트  (0) 2024.06.07

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

 

문제 키워드에 애드혹이 있던데 잘은 모르겠습니다.

제가 이 로직으로 접근하기 위해 생각한것 자체가 애드혹이 될 수는 있겠네요!

 

1. ab 는 좋은 문자열이다.
2. 만약 문자열 [S]가 좋은 문자열이라면, 오른쪽과 왼쪽 끝에 각각 a와 b를 추가한 문자열 a[S]b 또한 좋은 문자열이다.
3. 만약 문자열 [S]와 [T]가 좋은 문자열이라면 이들을 붙여 쓴 [S][T] 또한 좋은 문자열이다.

좋은 문자열의 정의가 이렇게 나옵니다.

그리고 입력으로 좋은 문자열 A, B가 주어지고, A -> B의 최소 연산 횟수를 구해야합니다.

 

우선 위 정의를 보고 알 수 있는건 "두 문자열의 구성은 같다" 입니다.

ab = S는 좋은 문자열이며, aSb도 좋은 문자열입니다. 그리고 SS도 좋은 문자열입니다.

여기서 사용된 a과 b의 갯수는 같습니다.

 

그렇다면 생각해볼 예외는 "두 문자열의 길이가 다른 경우" 입니다.

두 문자열의 길이가 다르다면 당연히 A -> B가 성립하지 않겠죠.

 

문제의 로직은

a, b의 갯수가 둘다 동일하기 때문에 b의 위치를 각각 찾습니다.

그리고 b 위치의 차이를 더합니다.

b의 갯수도 당연히 똑같기 때문에 하나라도 문자열의 길이를 벗어나면 종료할 수 있습니다.

 

 

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>
using namespace std;
 
string x, y;
int len1, len2;
 
void func() {
    if (len1 != len2) {
        cout << "-1\m";
        return;
    }
 
    int ret = 0;
    int idx1 = 0;
    int idx2 = 0;
    while (1) {
        while (idx1 < len1 && x[idx1] == 'a') idx1++;
        while (idx2 < len2 && y[idx2] == 'a') idx2++;
        if (idx1 >= len1 || idx2 >= len2) break;
        ret += abs(idx1++ - idx2++);
    }
    cout << ret << '\n';
}
 
void input() {
    cin >> x >> y;
    len1 = x.size();
    len2 = y.size();
}
 
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 > String' 카테고리의 다른 글

boj 9081 단어 맞추기  (0) 2021.12.15
boj 10096 세 친구  (0) 2021.01.29

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

 

단절점은 지웠을 때 그래프가 2개 이상으로 나눠지는 정점이라고 하고,

단절선은 지웠을 때 그래프가 2개 이상으로 나눠지는 간선이라고 합니다.

 

이 문제는 트리라고 명시되어 있기 때문에 애드혹으로 접근할 수 있습니다.

트리는 N - 1개의 간선만으로 N개의 정점을 연결하는 그래프입니다.

즉, 그래프에서 간선을 최소 갯수로 사용한게 트리입니다.

 

그렇다면 간선이 하나라도 없어진다면 더이상 하나의 그래프가 될 수 없습니다.

그러면 type == 2일 경우는 모두 yes를 출력하는걸로 마무리할 수 있습니다.

 

그 다음은 단절점인데

간선이 1개인 정점을 지우게 되더라도 여전히 하나의 그래프가 남게됩니다.

간선이 2개 이상인 정점을 지우게 되면 그 간선 만큼 2개 이상의 그래프로 나뉘는것을 알 수 있습니다.

 

그러면 정점을 지우면 해당 정점에 연결된 간선만큼 그래프가 나뉜다는 것을 알 수 있고,

type == 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
#include <iostream>
#include <vector>
#define MAX 100001
using namespace std;
 
int cnt[MAX];
int N, M;
 
void func() {
    int type, x;
    while (M--) {
        cin >> type >> x;
        if (type == 1) {
            if (cnt[x] > 1cout << "yes\n";
            else cout << "no\n";
        }
        else cout << "yes\n";
    }
}
 
void input() {
    int u, v;
    cin >> N;
    for (int i = 1; i < N; i++) {
        cin >> u >> v;
        cnt[u]++;
        cnt[v]++;
    }
    cin >> M;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 5527 전구 장식  (0) 2024.07.30
boj 14864 줄서기  (0) 2024.07.26
boj 2381 최대 거리  (0) 2024.07.25
boj 27468 2배 또는 0.5배  (0) 2024.07.22
boj 1377 버블 소트  (0) 2024.06.07

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

 

1 ~ N이 한번씩 등장하고, i, i + 1번째 수의 차이보다 i + 1, i + 2번째 수의 차이가 2배거나 0.5배인 수열을 만드는 문제입니다.

이 문제는 애드혹 문제로 직접 정답의 패턴을 찾을 수 있다면 구현 자체는 간단하게 할 수 있습니다.

 

저는 1부터 시작하여 정답을 찾던 도중

1 3 2 4 / 5 7 6 8 / 9 11 10 12 ... 이라는 패턴을 찾을 수 있었고, 4개 단위로 끊어서 정답을 구할 수 있다고 생각했습니다.

하지만 위의 정답은 반례가 하나 존재하며, N = 6, 10일 때를 보시면 7과 11이 6과 10보다 먼저 등장함을 알 수 있습니다.

따라서 N % 4 == 2인 경우와 N % 4 != 2인 경우를 분리하여 정답을 구했습니다.

 

N % 4 == 2일 때는 1 2 4 3 / 5 6 8 7 / 9 10 12 11 이라는 패턴을 찾을 수 있고, 이전 4개에서 +4를 한 값이 된다는 것을 알 수 있습니다.

그러면 찾은 패턴 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
#include <iostream>
using namespace std;
 
int list[4];
int N;
 
void init(int t) {
    if (t) {
        list[0= 1;
        list[1= 2;
        list[2= 4;
        list[3= 3;
    }
    else {
        list[0= 1;
        list[1= 3;
        list[2= 2;
        list[3= 4;
    }
    
}
 
void func() {
    init(N % 4 == 2);
 
    cout << "YES\n";
    int idx = 0;
    while (N--) {
        cout << list[idx] << ' ';
        list[idx] += 4;
        idx = (idx + 1) % 4;
    }
    cout << '\n';
}
 
void input() {
    cin >> N;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 5527 전구 장식  (0) 2024.07.30
boj 14864 줄서기  (0) 2024.07.26
boj 2381 최대 거리  (0) 2024.07.25
boj 14675 단절점과 단절선  (0) 2024.07.23
boj 1377 버블 소트  (0) 2024.06.07

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

 

수열이 a1, a2, ..., an 으로 주어졌을때 1 ~ ai 범위의 숫자를 적절하게 골라서 1 ~ N의 수가 하나씩 등장하도록 만들어야 합니다.

 

5
3 4 3 2 5

즉 이런 수열이 주어졌을 때

a1은 1 ~ 3 중에서 2

a2는 1 ~ 4 중에서 4

a3은 1 ~ 3 중에서 3

a4는 1 ~ 2 중에서 1

a5는 1 ~ 5 중에서 5

이렇게 골라서 1 ~ N까지 하나씩 등장하도록 순열을 만들 수 있습니다.

그리고 수열을 M번 수정하여 각각 수정된 수열에서도 위처럼 1 ~ N을 만들 수 있는지 구하는 문제입니다.

 

이 문제에서 저는 Segment Tree Lazy를 사용했고, 트리에는 해당 숫자를 몇 번 사용할 수 있는지 카운팅을 해줍니다.

1 ~ 5가 있다면

1은 1번, 2는 2번, 3은 3번, 4는 4번, 5는 5번 사용할 수 있습니다.

5 5 5 5 5가 입력된다면 5에서 수를 5번 고른다는 뜻입니다.

만약 4 4 4 4 4가 입력된다면 5개의 4에서 1 ~ 5를 하나씩 만들 수가 없게 되는거죠.

그렇게 i ~ N을 +1씩 업데이트를 해주고, 각각 노드에서의 최솟값으로 갱신하도록 합니다.

 

그 다음에는 최초로 주어진 수열에서 1 ~ N을 만들 수 있는지 먼저 확인합니다.

list[i]를 사용한게 되기 때문에 list[i] ~ N의 범위에 -1로 업데이트를 시켜줍니다.

트리를 최솟값으로 저장한 이유는 이 연산 과정에서 해당 값의 카운트가 음수인 경우를 빠르게 캐치하기 위해서입니다.

수열의 모든 수를 대상으로 업데이트가 끝난다면 query 함수를 이용하여 트리의 최솟값을 가져옵니다.

최솟값이 0 이상이면 순열을 만들 수 있고, 음수면 만들 수 없습니다.

 

다음 할 작업은 idx번째 수를 x로 변경하는 것입니다.

처음에는 위에서 -1로 업데이트를 한걸 되돌려야 하는지 고민했었는데 결과적으로 시간초과가 뜨더라고요!

N은 20만, M은 50만이기 때문에 시간초과가 당연한거였습니다.

20만개의 수를 50만번 업데이트 해야되니까요.. ㅎ

 

좀더 고민했을때는 트리에는 수열에 대한 카운팅이 각각 진행된 상태였고,

어차피 업데이트되는 idx번째 수에 대해서만 카운팅을 바꿔주면 된다는 결론이 나왔습니다.

그래서 수를 업데이트 하기 전에 기존 list[idx]의 범위에 대한 업데이트를 +1로 진행해주고,

새로운 값인 x의 범위에 대한 업데이트를 -1으로 진행해주면 새로운 수열에서의 카운팅 작업이 완료되는 것입니다.

마지막에는 업데이트된 트리의 최솟값을 가져와서 0 이상이면 `TAK`을, 음수면 `NIE`를 출력해주시면 되겠습니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <iostream>
#include <algorithm>
#define MAX 200001
using namespace std;
 
int list[MAX];
int tree[MAX << 2];
int lazy[MAX << 2];
int N, M;
 
void lazyUpdate(int node, int l, int r) {
    if (!lazy[node]) return;
    tree[node] += lazy[node];
    if (l != r) {
        lazy[node << 1+= lazy[node];
        lazy[(node << 1+ 1+= lazy[node];
    }
    lazy[node] = 0;
}
 
int update(int node, int l, int r, int s, int e, int diff) {
    lazyUpdate(node, l, r);
    if (l > e || s > r) return tree[node];
    if (s <= l && r <= e) {
        lazy[node] += diff;
        lazyUpdate(node, l, r);
        return tree[node];
    }
 
    int m = (l + r) >> 1;
    return tree[node] = min(update(node << 1, l, m, s, e, diff), update((node << 1+ 1, m + 1, r, s, e, diff));
}
 
int query(int node, int l, int r, int s, int e) {
    lazyUpdate(node, l, r);
    if (l > e || s > r) return 0;
    if (s <= l && r <= e) return tree[node];
 
    int m = (l + r) >> 1;
    return min(query(node << 1, l, m, s, e), query((node << 1+ 1, m + 1, r, s, e));
}
 
void init() {
    for (int i = 1; i <= N; i++) {
        update(11, N, i, N, 1);
    }
 
    for (int i = 0; i < N; i++) {
        update(11, N, list[i], N, -1);
    }
}
 
void print() {
    if (query(11, N, 1, N) >= 0cout << "TAK\n";
    else cout << "NIE\n";
}
 
void func() {
    init();
    print();
 
    int idx, x;
    while (M--) {
        cin >> idx >> x;
        update(11, N, list[idx - 1], N, 1);
        update(11, N, x, N, -1);
        list[idx - 1= x;
        print();
    }
}
 
void input() {
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> list[i];
    }
    cin >> M;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 4442 빌보의 생일  (0) 2024.08.01
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/17258

 

어디서 많이 본 문제네요!

월클병에 걸려버린 욱제가 파티 인원이 T명 이상일 때만 파티에 참여한다고 합니다.

친구를 최대 K명까지 투입하여 파티에 참여하는 시간을 최대로 만들어야 하는 문제입니다.

그 친구들은 친구들을 제외한 나머지 인원이 T명 이상이면 파티를 모두 나간다고 합니다.

그래서 적절한 시기에 친구들을 투입해야 최대를 구할 수 있습니다.

 

저는 dp를 활용했고, 파티 참여 인원을 구하기 위해 누적합을 추가했습니다.

imos 기법을 활용하여 시작과 끝 지점에만 카운팅을 해주고, 마지막에 누적합을 구해줍니다.

 

dp[N][K][pre]: N 시간에 친구 K명을 부를 수 있고, 파티에 참여중인 친구가 pre명일 때 파티에 머무르는 최대 시간

dp를 이렇게 구성했지만 2차원 배열로도 푸신분이 계시더라고요..? 전 모르겠습니다

 

문제에서 생각해볼 경우들은

1. 누적합이 이미 T 이상인지

2. 이전부터 참여중인 친구들과 합쳐서 T 이상인지

3. 파티를 유지할 수 있도록 친구들이 추가될 수 있는지

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

 

누적합이 이미 T 이상이라면 파티가 유지될 수 있으므로 +1을 하고 다음 시간을 확인합니다.

이때 친구들을 제외한 인원들이 T 이상이므로 참여하고 있던 친구들은 모두 빠져나갑니다.

 

이전부터 참여중인 친구들과 합쳐서 T 이상이라면 파티가 유지될 수 있으므로 +1을 하고 다음 시간을 확인합니다.

이때 친구들을 제외하면 T보다 작으므로 참여하고 있던 친구들은 계속 참여합니다.

 

파티를 유지할 수 있도록 친구들이 추가될 수 있다면 부족한 인원을 채워줄지, 아니면 친구들을 추가하지 않고 다음 시간을 확인할 지 선택합니다.

친구들을 추가한다면 +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
#include <iostream>
#include <cstring>
#include <algorithm>
#define MAX 310
using namespace std;
 
int dp[MAX][MAX][MAX];
int sum[MAX];
int N, M, K, T;
 
int solve(int idx, int k, int pre) {
    if (idx == N + 1return 0;
 
    int& ret = dp[idx][k][pre];
    if (ret != -1return ret;
 
    if (sum[idx] >= T) {
        return ret = solve(idx + 1, k, 0+ 1;
    }
    if (sum[idx] + pre >= T) {
        return ret = solve(idx + 1, k, pre) + 1;
    }
 
    ret = solve(idx + 1, k, pre);
    int tmp = T - (sum[idx] + pre);
    if (tmp <= k) {
        ret = max(ret, solve(idx + 1, k - tmp, pre + tmp) + 1);
    }
    return ret;
}
 
void init() {
    memset(dp, -1sizeof(dp));
    for (int i = 1; i < MAX; i++) {
        sum[i] += sum[i - 1];
    }
}
 
void func() {
    init();
    cout << solve(1, K, 0<< '\n';
}
 
void input() {
    int l, r;
    cin >> N >> M >> K >> T;
    while (M--) {
        cin >> l >> r;
        sum[l]++;
        sum[r]--;
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 19623 회의실 배정 4  (0) 2024.07.29
boj 30461 낚시  (0) 2024.07.28
boj 1398 동전 문제  (0) 2024.07.04
boj 1229 육각수  (0) 2024.06.30
boj 25378 조약돌  (0) 2024.06.29

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

 

처음 이문제에 접근했을때 문제를 이해하는데 진짜 오래걸렸던 기억이 있어서 다시 풀어봤습니다.

다행히도 백준에 실려있는 문제는 설명이 잘되어있어서 이해하기가 수월하더라고요! 물론 두번째 보는거라 그런거긴 한데

그래도 확실히 게임이론은 어려운것 같습니다..

 

이 문제는 S에서 출발하여 말을 한번씩 번갈아가면서 옮길 수 있고, E에 먼저 도착하는 사람이 이기는 게임으로 선공을 할지, 후공을 할지 구하는 문제입니다.

dfs를 통해서 문제를 접근했고, E에 도착하면 true를 리턴해줍니다.

그다음 자식 노드들 중에서 true인 경우가 하나라도 있으면 true를 리턴해주시면 됩니다

 

"더는 말을 움직일 수 없게 되면 게임이 종료됩니다."

라는 말이 문제에 명시되어 있어서 상대가 자식노드가 없는 정점으로 보내버린다면 게임을 지게됩니다.

이 예외까지 생각하여 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
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <vector>
#define MAX 100001
using namespace std;
 
vector<int> graph[MAX];
bool visit[MAX];
int N, S, E, ret;
 
bool dfs(int v, int t) {
    if (v == E) return true;
    visit[v] = true;
 
    int cnt = 0;
    bool flag = false;
    for (auto next : graph[v]) {
        if (visit[next]) continue;
        flag |= dfs(next, 1 - t);
        cnt++;
    }
    if (t && cnt > 1return false;
 
    return flag;
}
 
void func() {
    if (dfs(S, 0)) cout << "First\n";
    else cout << "Second\n";
}
 
void input() {
    int u, v;
    cin >> N >> S >> E;
    for (int i = 1; i < N; i++) {
        cin >> u >> v;
        graph[u].push_back(v);
        graph[v].push_back(u);
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 19542 전단지 돌리기  (0) 2024.06.19
boj 19236 청소년 상어  (0) 2021.04.22
boj 2239 스도쿠  (0) 2021.04.16
boj 2458 키 순서  (0) 2021.04.13
boj 14889 스타트와 링크  (0) 2021.03.17

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

 

1. 상하좌우로 이동한다. 만약 이동할 곳이 격자 밖이라면 이동할 수 없다. 
2. 이동한 곳이 안전지대라면 반복을 종료한다.
3. 이동한 곳에 우산이 있다면 우산을 든다. 만약, 이동할 때부터 우산을 가지고 있다면 가지고 있는 우산을 버리고 새로운 우산으로 바꾼다.

     버린 우산은 더 이상 사용할 수 없다.
4. 죽음의 비를 맞았을 때, 우산을 가지고 있다면 우산의 내구도가 1이 감소하고 만약 우산을 가지고 있지 않는다면 체력이 1 감소한다.
5. 만약 우산의 내구도가 0이 되면 들고 있는 우산이 사라진다.
6. 만약 체력이 0이 되면 죽는다.
7. 아직 체력이 남았다면 안전지대까지 위 과정을 반복한다.

 

격자 내 움직임은 위와 같은 순서로 진행됩니다.

S에서 출발하여 안전지대인 E로 도착하는 최소 거리를 구하는 문제입니다.

그리고 S에도 출발하는 직후부터는 비가 내린다고 명시되어 있습니다.

현재 체력 H와 우산의 내구도 D가 주어지며, 격자 내에는 우산이 놓여진 곳이 있습니다.

 

우선 최소거리를 구하기 위해 bfs를 사용합니다.

그리고 다음 좌표를 이동할 수 있는지 확인합니다.

내구도 1 이상인 U인 지점은 우산이 있고, 도착 지점인 E는 비가 오지 않으므로 무조건 지나갈 수 있습니다.

나머지 지점은 비가 오기 때문에 체력이 1보다는 많아야 지나갈 수 있습니다. (정확하게는 체력 + 들고 있는 우산의 남은 내구도)

 

U가 있는 지점에 도착한다면 우산의 내구도를 D로 초기화 해주시고, E가 아닌 모든 지점에서 우산의 내구도 or 체력을 1씩 감소시킵니다.

그리고 남은 체력 + 들고있는 우산의 내구도가 visit[nx][ny]보다 큰지 비교합니다.

해당 지점을 방문했더라도 체력의 상태가 다르기 때문에 방문처리를 int로 하게 되었습니다.

visit[nx][ny] >= nh + nu이면, 이전 단계보다 좋지 않은 조건으로 이동하는거라 제외를 시켜주시면 되고, 해당 visit 배열을 더 큰 값으로 갱신시켜주도록 합니다.

(중간에 체력이 0이 되었어도 visit 배열 자체가 0으로 초기화되어 있기 때문에 자동으로 걸러집니다.)

 

이 과정을 반복하여 E에 도착했다면 t를 리턴시켜주고, 반복문이 종료되어도 답을 구하지 못했다면 지나갈 수 없다는 뜻이므로 -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
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream>
#include <queue>
#define MAX 501
using namespace std;
typedef pair<intint> pii;
 
char list[MAX][MAX];
int visit[MAX][MAX];
int direct[4][2= { {0,1},{1,0},{0,-1},{-1,0} };
int N, H, D;
int sx, sy;
 
bool isMove(int x, int y, int hd) {
    if (list[x][y] == 'U' || list[x][y] == 'E'return true;
    return hd > 1;
}
 
int bfs() {
    queue<pair<pii, pii> > q;
    q.push({ {H, 0}, {sx, sy} });
    visit[sx][sy] = H;
    for (int t = 0!q.empty(); t++) {
        int qsize = q.size();
        while (qsize--) {
            int x = q.front().second.first;
            int y = q.front().second.second;
            int h = q.front().first.first;
            int u = q.front().first.second;
            q.pop();
 
            if (list[x][y] == 'E') {
                return t;
            }
 
            for (int d = 0; d < 4; d++) {
                int nx = x + direct[d][0];
                int ny = y + direct[d][1];
                int nh = h;
                int nu = u;
 
                if (nx < 0 || ny < 0 || nx >= N || ny >= N) continue;
                if (!isMove(nx, ny, h + u)) continue;
                
                if (list[nx][ny] == 'U') {
                    nu = D;
                }
                if (list[nx][ny] != 'E') {
                    if (nu) nu--;
                    else nh--;
                }
                if (visit[nx][ny] >= nh + nu) continue;
 
                q.push({ {nh, nu}, {nx,ny} });
                visit[nx][ny] = nh + nu;
            }
        }
    }
 
    return -1;
}
 
void func() {
    cout << bfs() << '\n';
}
 
void input() {
    cin >> N >> H >> D;
    for (int i = 0; i < N; i++) {
        cin >> list[i];
        for (int j = 0; j < N; j++) {
            if (list[i][j] == 'S') {
                sx = i;
                sy = j;
            }
        }
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 27211 도넛 행성  (2) 2024.07.17
boj 19940 피자 오븐  (0) 2024.07.16
boj 2310 어드벤처 게임  (0) 2024.06.25
boj 2412 암벽 등반  (0) 2024.06.22
boj 14497 주난의 난(難)  (0) 2024.06.21

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

 

정말 흔한 bfs문제로 영역 구하기입니다.

list[i][j] = 0인 좌표들을 bfs를 통해 인접해있는 영역의 갯수를 구하면 됩니다.

거기에 추가로 다음 좌표가 맵 밖으로 벗어난다면 반대쪽 끝으로만 이동시켜주면 이 문제를 쉽게 해결할 수 있습니다.

 

 

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
#include <iostream>
#include <queue>
#define MAX 1001
using namespace std;
typedef pair<intint> pii;
 
int list[MAX][MAX];
bool visit[MAX][MAX];
int direct[4][2= { {0,1},{1,0},{0,-1},{-1,0} };
int N, M;
 
void bfs(int sx, int sy) {
    queue<pii> q;
    q.push({ sx, sy });
    visit[sx][sy] = true;
    while (!q.empty()) {
        auto xy = q.front();
        q.pop();
 
        for (int d = 0; d < 4; d++) {
            int nx = xy.first + direct[d][0];
            int ny = xy.second + direct[d][1];
 
            if (nx < 0) nx = N - 1;
            if (ny < 0) ny = M - 1;
            if (nx >= N) nx = 0;
            if (ny >= M) ny = 0;
            if (visit[nx][ny] || list[nx][ny]) continue;
 
            q.push({ nx,ny });
            visit[nx][ny] = true;
        }
    }
}
 
void func() {
    int ret = 0;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            if (visit[i][j] || list[i][j]) continue;
            ret++;
            bfs(i, j);
        }
    }
    cout << ret << '\n';
}
 
void input() {
    cin >> N >> M;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) {
            cin >> list[i][j];
        }
    }
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    input();
    func();
 
    return 0;
}
cs

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

boj 22944 죽음의 비  (0) 2024.07.18
boj 19940 피자 오븐  (0) 2024.07.16
boj 2310 어드벤처 게임  (0) 2024.06.25
boj 2412 암벽 등반  (0) 2024.06.22
boj 14497 주난의 난(難)  (0) 2024.06.21

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

 

처음 이문제를 봤을때 N의 범위가 10000000이라 set을 써야하나 고민했었습니다.

근데 한 번에 이동할 수 있는건 (+60, +10, -10, +1, -1) 이렇게 5개밖에 없기 때문에 언제 1000만을 찍나 고민했습니다.

일단 바로 알수있는건 N이 클수록 +60을 많이 누르는게 이득이지 않을까 생각하여 bfs에 greedy까지 생각하게 되었습니다.

 

여기까지 오셨다면 문제를 쉽게 해결할 수 있습니다.

N을 60으로 나눈다면 우리가 할건  0 ~ 59까지의 최소 횟수만 구하면 됩니다.

방문처리는 N에 대해서만 해주시면 되고, queue에는 N과 버튼 누른 횟수를 모두 넣어줬습니다.

문제에서 횟수가 동일하다면 (-1 > +1 > -10 > +10 > +60) 순으로 높은 우선순위를 부여한다고 명시되어 있기 때문에

연산의 순서 또한 (-1 -> +1 -> -10 -> +10 -> +60) 으로 진행합니다.

다음에는 N을 입력받고, +60에는 N / 60을 더해주고, 나머지는 미리 구했던 값을 그대로 출력해주시면 되겠습니다.

 

개인적으로 이 문제 어렵다고 느껴졌는데 골드5 더라고요..?

난이도 기여하러 갔더니 다들 쉽다는 반응은 아니었는데 골드5를 주셨더라고요..??

난 어렵게 느껴졌는데.. 골드4보다 더 주고싶었는데.. 그냥 그렇다고요..

 

 

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
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#define MAX 61
using namespace std;
 
bool visit[MAX];
int btns[5= { 6010-101-1 };
vector<int> ret[MAX];
int N;
 
void bfs() {
    queue<pair<intvector<int>> > q;
    ret[0= vector<int>(50);
    q.push({ 0,ret[0] });
    visit[0= true;
    while (!q.empty()) {
        auto now = q.front();
        q.pop();
 
        for (int d = 4; d >= 0; d--) {
            int next = now.first + btns[d];
            if (next < 0 || next > 60continue;
            if (visit[next]) continue;
 
            visit[next] = true;
            ret[next] = now.second;
            ret[next][d]++;
            q.push({ next, ret[next] });
        }
    }
}
 
void func() {
    int x = N / 60;
    int y = N % 60;
    cout << x + ret[y][0<< ' ' << ret[y][1<< ' ' << ret[y][2<< ' ' << ret[y][3<< ' ' << ret[y][4<< '\n';
}
 
void input() {
    cin >> N;
}
 
int main() {
    cin.tie(NULL); cout.tie(NULL);
    ios::sync_with_stdio(false);
 
    bfs();
 
    int tc;
    cin >> tc;
    while (tc--) {
        input();
        func();
    }
 
    return 0;
}
cs

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

boj 22944 죽음의 비  (0) 2024.07.18
boj 27211 도넛 행성  (2) 2024.07.17
boj 2310 어드벤처 게임  (0) 2024.06.25
boj 2412 암벽 등반  (0) 2024.06.22
boj 14497 주난의 난(難)  (0) 2024.06.21

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

 

학교(S)에서 출발하여 각 아파트 단지에 있는 모든 학생들을 버스로 태우고 들어와야 합니다.

버스에는 정원이 K명이라 같은 아파트 단지를 여러번 왔다가는 상황이 있습니다.

그렇다면 최대한 가까운 거리를 왔다갔다 하는 편이 더 이득이라고 볼 수 있습니다.

즉 최대한 멀리있는 학생부터 데려오도록 합니다.

 

우선 학생들의 위치는 학교 기준 좌, 우로 나눠져 있습니다.

그렇기 때문에 왼쪽에 있는 학생들 먼저 다 데려온 후 오른쪽 학생들을 다 데려오는 방식으로 접근할 수 있습니다. (반대도 상관 없습니다.)

 

idx는 현재 데려올 수 있는 학생들 중 가장 멀리있는 학생의 인덱스입니다.

왕복하여 데려오기 때문에 idx번째 학생과의 거리 * 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
50
51
52
53
54
55
#include <iostream>
#include <algorithm>
#define MAX 30001
using namespace std;
typedef pair<intint> pii;
 
pii list[MAX];
int N, K, S;
 
void func() {
    sort(list, list + N);
 
    int idx = 0;
    int ret = 0;
    while (idx < N && list[idx].first < S) {
        ret += ((S - list[idx].first) << 1);
        int cnt = 0;
        while (idx < N && list[idx].first < S && cnt < K) {
            int tmp = min(K - cnt, list[idx].second);
            cnt += tmp;
            list[idx].second -= tmp;
            if (!list[idx].second) idx++;
        }
    }
 
    idx = N - 1;
    while (idx >= 0 && list[idx].first > S) {
        ret += ((list[idx].first - S) << 1);
        int cnt = 0;
        while (idx >= 0 && list[idx].first > S && cnt < K) {
            int tmp = min(K - cnt, list[idx].second);
            cnt += tmp;
            list[idx].second -= tmp;
            if (!list[idx].second) idx--;
        }
    }
    cout << ret << '\n';
}
 
void input() {
    cin >> N >> K >> S;
    for (int i = 0; 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 > Greedy' 카테고리의 다른 글

boj 15553 난로  (0) 2024.07.13
boj 1437 수 분해  (0) 2024.07.12
boj 23559 밥  (0) 2024.07.11
boj 2140 지뢰찾기  (0) 2024.07.10
boj 1132 합  (0) 2024.07.08

+ Recent posts