Suhwanc

대회 링크 : atcoder.jp/contests/abc182

 

A - twiblr

[문제]

twiblr 라는 sns 에서 추가할 수 있는 최대 팔로잉 수를 구하라.

 

[풀이]

문제에서 나온 그대로

A, B가 주어졌을 때 (2 * A + 100) - B 를 구하는 간단한 문제입니다.

 

[코드]

1
2
3
4
5
6
7
8
9
10
11
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    int a, b; cin >> a >> b;
    cout << (a * 2+ 100 - b << "\n";
    return 0;
}
cs

 

B - Almost GCD

[문제]

정수로 이루어진 길이가 N인 수열 A가 있을 때

2이상의 정수 중 가장 수열 A의 원소들과 많이 나누어 떨어지는 수 k를 구하라. 

 

[풀이]

우선 k를 구하기 위해서 최대공약수(GCD)의 성질을 생각해보면

어떤 수 Ai에 대한 gcd k는 Ai보다 반드시 작아야 한다는 걸 알 수 있습니다.

 

그렇다면, 문제 제한 상 Ai는 max 1000 밖에 안되기 때문에 그냥 2 ~ 1000 까지 N만큼 순회하며

몇 개나 나누어 떨어지는지 구하면 됩니다.

 

시간 복잡도 : O(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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    int n; cin >> n;
    vector<int>v(n);
    for (int i = 0; i < n; i++cin >> v[i];
    vector<pair<intint> >ans;
    for (int i = 2; i <= 1000; i++)
    {
        int cnt = 0;
        for (int j = 0; j < n; j++)
        {
            if (v[j] % i == 0) cnt++;
        }
        ans.push_back({ cnt, i });
    }
    sort(ans.begin(), ans.end());
    cout << ans[ans.size() - 1].second << "\n";
    return 0;
}
cs

 

C - To3

[문제]

"0"이 없는 정수 N이 주어졌을 때

아무데나 1자리씩 지울 수 있는 활동을 할 수 있다고 가정하자.

정수 N이 3의 배수가 되기 위해서 이 활동을 최소 몇 번 해야하는가?

 

[풀이]

저번 atcoder #181 에서도 나온 문제랑 비슷한데, atcoder는 n의 배수를 좀 좋아하는 듯 합니다.

이 문제를 풀기 위해서도 역시 3의 배수 판별법을 알아야 하는데, 생각보다 간단합니다.

 

3의 배수 판별법 : 모든 자릿수의 합이 3이 배수이면 됩니다.

 

따라서 최대 long long 범위인 N을 string으로 받고, 각 자릿수를 더한 값을 sum이라 하면,

 

case1) sum % 3 == 0 

더 볼 것도 없이 N은 이미 3의 자릿수입니다.

 

case2) sum % 3 == 1 

이 경우 정수 N을 구성하는 자릿 수 중 3으로 나눈 나머지가 1인 수 (1, 4, 7)를 찾으면 답이 1이고, 만약 없다면 3으로 나눈 나머지가 2인 수를 2개 찾으면 답을 2로 고정시킬 수 있습니다. ex. (5 + 8) % 3 == 1

 

case3) sum % 3 == 2

case 2와 비슷한데, 찾아야하는 나머지 수를 반대로 바꾸면 됩니다.

 

-> 만약 case 2, 3에서 이들을 찾을 수는 있는데, 이들을 빼버리면 아예 N이 사라지는 경우 -1을 답으로 도출하면 됩니다. ex) 11

 

시간복잡도 : O(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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    string s; cin >> s;
    int sum = 0;
    for (int i = 0; i < s.size(); i++) sum += s[i] - '0';
    
    int ans = 0;
    if (sum % 3 == 1)
    {
        int tmp_sum = sum;
        int cnt = 0;
        bool flag = false;
        for (int i = 0; i < s.size(); i++) {
            if ((s[i] - '0') % 3 == 1) {
                if (sum - (s[i] - '0'< 3continue;
                flag = true;
                ans = 1;
                break;
            }
        }
        if (!flag)
        {
            for (int i = 0; i < s.size(); i++)
            {
                if ((s[i] - '0') % 3 == 2) cnt++;
            }
            if (cnt >= 2) {
                if (s.size() > 2) {
                    ans = 2;
                    flag = true;
                }
            }
        }
        if (!flag) ans = -1;
    }
    else if (sum % 3 == 2)
    {
        int cnt = 0;
        bool flag = false;
        for (int i = 0; i < s.size(); i++) {
            if ((s[i] - '0') % 3 == 2) {
                if (sum - (s[i] - '0'< 3continue;
                flag = true;
                ans = 1;
                break;
            }
        }
        if (!flag)
        {
            for (int i = 0; i < s.size(); i++)
            {
                if ((s[i] - '0') % 3 == 1) cnt++;
            }
            if (cnt >= 2) {
                if (s.size() > 2) {
                    ans = 2;
                    flag = true;
                }
            }
        }
        if (!flag) ans = -1;
    }
    cout << ans << "\n";
    return 0;
}
cs

 

D - Wandering

 

[문제]

음수가 포함된 정수 N개로 구성된 수열 A가 주어졌을 때

로봇은 직선 좌표계 상에서 다음과 같은 활동을 할 수 있다.

 

  • A1 만큼 좌표 이동
  • A1 만큼 이동 후, A2 만큼 이동
  • A1, A2 ... An 만큼 이동

이 과정속에서 로봇의 최대 좌표값을 구하시오.

 

[풀이]

이 문제는 좌표계라는 것을 이해하는데 시간이 조금 걸렸네요

 

우선 문제 조건 상 N 제한이 20만이기 때문에

완전탐색 형식으로 한칸, 한칸 전진하는 방식을 사용하면 O(N!) 시간복잡도가 걸립니다. 그럴 필요도 없구요

 

핵심은 처음엔 A1, 두 번째는 A1 ~ A2, 세 번째엔 A1 ~ A3 ... A1 ~ An 과 같이

순서대로 한 칸씩 더한다는 점을 보고 부분합 문제임을 파악하면 됩니다.

 

따라서 pre[i] : 0 ~ i 번 index까지의 합을 미리 구해두면

n번째까지 로봇이 움직임을 수행했을 때, 위치하는 좌표값을 O(1) 시간에 파악가능합니다.

 

하지만 여기 함정이 조금 있는데, 로봇이 움직임을 수행하는 중에 최대값이 나올 수도 있다는 점입니다.

 

만약 A = [2, -1, -2] 인 경우

pre = [2, 1, -1] 이라 1차원적으로 보면 최대값은 3이지만

로봇이 2번째까지 움직인 후(3인 상태), 한 칸 전진하면 최대값인 5가 나올 수 있습니다.

따라서, 부분합들 중 최대값을 구하고, 정답에 업데이트 시키면 됩니다.

p_max[i] = max(pre[0] ~ pre[i])

 

시간복잡도 : O(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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
ll pre[200010];
ll p_max[200010];
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    int n; cin >> n;
    vector<ll>v(n);
    for (int i = 0; i < n; i++cin >> v[i];
    pre[0= v[0]; p_max[0= v[0];
    for (int i = 1; i < n; i++) {
        pre[i] = pre[i - 1+ v[i];
        p_max[i] = max(p_max[i - 1], pre[i]);
    }
    ll ans = 0, tmp_ans = 0;
    for (int i = 0; i < n; i++)
    {
        tmp_ans += p_max[i];
        ans = max(tmp_ans, ans);
        tmp_ans -= p_max[i];
        tmp_ans += pre[i];
    }
    cout << ans << "\n";
    return 0;
}
cs

 

E - Akari

[문제]

H * w 격자판과 그 안에
동서남북 4방향을 비추는 전구의 위치와, 전구를 차단하는 벽의 위치가 주어진다.

전구는 4방향으로 벽이 만날 때 까지 인접한 벽을 비출 수 있다고 한다.

 

이 경우, 전구가 있는 곳을 포함하여 전구가 통하는 벽은 총 몇 칸인가?

 

[풀이]

우선, 문제 제한부터 보자면 격자판은 최대 1500 * 1500 개의 칸을 가질 수 있습니다.

또한 전구의 위치는 최대 50만개라서

전구의 위치마다, bfs 형식으로 4방향을 비추면 최악의 경우 O(1500^3) 정도의 시간이 걸립니다.

따라서, 이 문제 또한 전처리를 잘 해주어야하는데

 

격자판의 각 칸을 T라고 하면, 이 칸은 좌표 뿐만 아니라 여러가지 정보를 담고 있다고 합시다.

 

  • 전구의 불빛이 상, 하, 좌, 우로 빠져나간 적이 있는지?
  • 전구가 위치하는지, 벽이 위치하는지, 아니면 비어있는 공간인지
  • 불빛이 들어온 적이 있는지

 

여기서 전구의 불빛이 빠져나간지 확인하는 것이 핵심인데

만약 모든 칸에 전구가 있는 4 by 4 격자판이 있는 경우

(1, 1)에서 오른쪽으로 불빛을 이미 쏜 것을 확인했는데, (1,2)에서 다시 한 번 확인하지 않게 해주는 효과가 있습니다.

이는 격자판이 더 커지면 커질 수록 상당히 시간복잡도가 단축되겠죠.

이 방식을 이용하면 각 칸마다 최대 4번만 확인하면 되기 때문에

시간복잡도는 최대 O(4 * N^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
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
89
90
91
92
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
typedef struct box {
    bool up = false;
    bool down = false;
    bool left = false;
    bool right = false;
    bool state = false;
    int cmd = 0//(0 : 아무것도없음, 1: 전구, 2: 벽)
};
box arr[1501][1501];
int h, w, n, m;
 
// 상, 하, 좌, 우
int dy[] = { -1100 };
int dx[] = { 00-11 };
 
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    cin >> h >> w >> n >> m;
    for (int i = 0; i < n; i++)
    {
        int y, x; cin >> y >> x;
        arr[y-1][x-1].cmd = 1;
        arr[y-1][x-1].state = true;
    }
    for (int i = 0; i < m; i++)
    {
        int y, x; cin >> y >> x;
        arr[y-1][x-1].cmd = 2;
    }
 
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            if (arr[i][j].cmd == 1)
            {
                arr[i][j].up = true;
                arr[i][j].down = true;
                arr[i][j].left = true;
                arr[i][j].right = true;
                
                //up
                for (int k = i - 1; k >= 0; k--)
                {
                    if (arr[k][j].cmd == 2break;
                    if (arr[k][j].up) break;
                    arr[k][j].up = true;
                    arr[k][j].state = true;
                }
                //down
                for (int k = i + 1; k < h; k++)
                {
                    if (arr[k][j].cmd == 2break;
                    if (arr[k][j].down) break;
                    arr[k][j].down = true;
                    arr[k][j].state = true;
                }
                //left
                for (int k = j - 1; k >= 0; k--)
                {
                    if (arr[i][k].cmd == 2break;
                    if (arr[i][k].left) break;
                    arr[i][k].left = true;
                    arr[i][k].state = true;
                }
                //right
                for (int k = j + 1; k < w; k++)
                {
                    if (arr[i][k].cmd == 2break;
                    if (arr[i][k].right) break;
                    arr[i][k].right = true;
                    arr[i][k].state = true;
                }
            }
        }
    }
    int ans = 0;
    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            if (arr[i][j].state) ans++;
        }
    }
    cout << ans << "\n";
    return 0;
}
cs

 

F - Valid payments

F번은 에디토리얼 풀리면 적어두겠습니다!