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

 

1938번: 통나무 옮기기

첫째 줄에 주어진 평지의 한 변의 길이 N이 주어진다. (4<=N<=50) 주어진다. 이어서 그 지형의 정보가 0, 1, B, E로 이루어진 문자열로 주어진다. 한 줄에 입력되는 문자열의 길이는 N이며 입력 문자 사이에는 빈칸이 없다. 통나무와 최종 위치의 개수는 1개이다.

www.acmicpc.net

<풀이>

1. 초기 통나무 상태를 구하고, bfs를 시작한다.

2. 조건에 맞춰서 통나무를 움직이고, 도착지점에 도달하면 함수를 종료한다.

3. 움직인 횟수를 출력한다.

<해법>

1. BFS

=> 문제에서 BFS를 이용해야겠다는 것은 알 수 있습니다. 이제 중요한 것은 큐에 담을 자료형방문 처리 입니다.

결국, 어떻게 큐에 담아서( = 큐에 담을 자료형) 그 중 의미있는 정보만을 골라서( = 방문하지 않은 정보를 고름) 다시 큐에 담아 탐색을 진행하는 방법을 생각해야 합니다.

- 큐에 담을 자료형

bfs를 동작할 때 필요한 정보들을 담았습니다.

위 문제에서는 가장먼저 통나무 세 개의 좌표들이 필요합니다. 또한 회전을 해야하므로 눕혀있는 상태, 출력할 값인 움직인 횟수가 필요합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//좌표
struct pos {
    int x, y;
};
 
//움직일 통나무 3개
struct logs {
 
    //각각 통나무 좌표
    pos A, B, C;
 
    //통나무가 눕혀있는 상태
    int floating;
 
    //움직인 횟수
    int moveCnt;
};

- 방문 처리

완전 탐색인 bfs를 효율적으로 사용하기 위해서는 필요없는 탐색을 줄이는 것이 중요합니다.

필요없는 탐색이란 탐색할 범위를 넘어섰거나, 이전에 탐색했던 것을 탐색하는 것입니다.

그 중 두번째 비효율 탐색을 줄이는 방법으로, 방문 처리가 있습니다.

위 문제를 예로 들겠습니다.

만약 문제에서 방문처리를 3개 통나무의 좌표로 하게 된다면, 아래와 같은 비효율 탐색이 발생한다.

따라서, 방문처리는 가운데 통나무(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
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
 
//좌표
struct pos {
    int x, y;
};
 
//움직일 통나무 3개
struct logs {
 
    //각각 통나무 좌표
    pos A, B, C;
 
    //통나무가 눕혀있는 상태
    int floating;
 
    //움직인 횟수
    int moveCnt;
};
 
//앞 4개 : 상하좌우, 뒤 4개 : 대각방향
int di[] = { -1,0,1,0,-1,1,1,-1 };
int dj[] = { 0,1,0,-1,1,1,-1,-1 };
 
int N;
char map[52][52];
 
//visited[가운데 통나무 행][가운데 통나무 열][눕혀있는 상태]
bool visited[52][52][2];
 
queue<logs> q;
int res = 0;
 
bool inner(int ai, int aj, int bi, int bj, int ci, int cj) {
    if (ai <= 0 || aj <= 0 || bi <= 0 || bj <= 0 || ci <= 0 || cj <= 0 || ai > N || aj > N || bi > N || bj > N || ci > N || cj > N) {
        return false;
    }
    return true;
}
 
void bfs() {
    while (!q.empty()) {
        logs cur = q.front();
        q.pop();
 
        //3개 모두 옮겼을 시 - 종료
        if (map[cur.A.x][cur.A.y] == 'E' && map[cur.B.x][cur.B.y] == 'E' && map[cur.C.x][cur.C.y] == 'E') {
            res = cur.moveCnt;
            return;
        }
 
        //상,하,좌,우로 움직일 수 있는지 판단 후 큐에 담기
        for (int i = 0; i < 4; i++) {
            logs nxt = cur;
            nxt.A.x += di[i];
            nxt.A.y += dj[i];
            nxt.B.x += di[i];
            nxt.B.y += dj[i];
            nxt.C.x += di[i];
            nxt.C.y += dj[i];
 
            //옮긴 3개 통나무의 좌표가 범위 안에 있는지 판단
            if (!inner(nxt.A.x, nxt.A.y, nxt.B.x, nxt.B.y, nxt.C.x, nxt.C.y)) {
                continue;
            }
 
            //옮긴 3개 통나무 좌표에 다른 나무가 있거나, 이전에 같은 모양으로 방문한적이 있는지 판단
            if (map[nxt.A.x][nxt.A.y] == '1' || map[nxt.B.x][nxt.B.y] == '1' || map[nxt.C.x][nxt.C.y] == '1' || visited[nxt.B.x][nxt.B.y][nxt.floating]) {
                continue;
            }
 
            nxt.moveCnt++;
            visited[nxt.B.x][nxt.B.y][nxt.floating] = true;
            q.push(nxt);
        }
 
        //회전할 수 있는지 판단 후 큐에 담기
        bool canRotate = true;
        logs nxt = cur;
        for (int i = 0; i < 8; i++) {
            
            //중간 통나무에서 상,하,좌,우,대각선 4방향 탐색 - 모든 방향이 범위 안에 있거나, 다른 나무가 없다면 회전 가능
            int bni = nxt.B.x + di[i];
            int bnj = nxt.B.y + dj[i];
 
            if (bni <= 0 || bnj <= 0 || bni > N || bnj > N || map[bni][bnj] == '1') {
                canRotate = false;
                break;
            }
        }
 
        //회전할 수 있다면
        if (canRotate) {
 
            //통나무가 세로로 있는 경우, 가로로 돌리기
            if (nxt.floating == 0) {
                nxt.A.x = nxt.B.x;
                nxt.A.y = nxt.B.y - 1;
                nxt.C.x = nxt.B.x;
                nxt.C.y = nxt.B.y + 1;
                nxt.floating = 1;
            }
            //통나무가 가로로 있는 경우, 세로로 돌리기
            else {
                nxt.A.x = nxt.B.x - 1;
                nxt.A.y = nxt.B.y;
                nxt.C.x = nxt.B.x + 1;
                nxt.C.y = nxt.B.y;
                nxt.floating = 0;
            }
 
            if (!visited[nxt.B.x][nxt.B.y][nxt.floating]) {
                nxt.moveCnt++;
                visited[nxt.B.x][nxt.B.y][nxt.floating] = true;
                q.push(nxt);
            }
        }
    }
    return;
}
 
int main() {
 
    //시작 통나무
    logs st;
    vector<pos> startLogs;
    int startLogsFloat = 0;
 
    //입력
    cin >> N;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) {
            cin >> map[i][j];
            if (map[i][j] == 'B') {
                startLogs.push_back({ i,j });
            }
        }
    }
 
    //첫번째 두번쨰 통나무 열 비교 - 눕혀있는 상태값 찾기
    if (startLogs[1].y - startLogs[0].y == 1) {
        startLogsFloat = 1;
    }
    
    //시작 통나무 = {A통나무 좌표, B통나무 좌표, C통나무 좌표, 눕혀있는 상태, 옮긴 횟수}
    st = { {startLogs[0].x, startLogs[0].y}, {startLogs[1].x, startLogs[1].y}, {startLogs[2].x, startLogs[2].y}, startLogsFloat, 0 };
    visited[st.B.x][st.B.y][startLogsFloat] = true;
    q.push(st);
    bfs();
 
    //출력
    cout << res;
}

 

BFS와 시뮬레이션에 대해 알아볼 수 있는 문제였습니다.

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

 

2174번: 로봇 시뮬레이션

문제 가로 A(1≤A≤100), 세로 B(1≤B≤100) 크기의 땅이 있다. 이 땅 위에 로봇들이 N(1≤N≤100)개 있다. 로봇들의 초기 위치는 x좌표와 y좌표로 나타난다. 위의 그림에서 보듯 x좌표는 왼쪽부터, y좌표

www.acmicpc.net

<풀이>

1. 주어진 입력 값들을 이해하기 쉽게 정리한다.

2. 명령하나씩 실행시키고, 결과를 출력한다.

<해법>

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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <iostream>
using namespace std;
 
int di[] = { -1,0,1,0 };
int dj[] = { 0,1,0,-1 };
 
//가로,세로,로봇 개수,명령 개수
int A, B, N, M;
 
//로봇이 움직이는 맵
int map[102][102];
//로봇마다 정보(행,열,방향)
int robotInfo[101][3];
 
bool crash = false;
 
bool simulation(int id, char cmd, int cmdCnt) {
 
    //명령내릴 로봇 위치,방향 저장
    int tmpi = robotInfo[id][0];
    int tmpj = robotInfo[id][1];
    int tmpDir = robotInfo[id][2];
 
    map[tmpi][tmpj] = 0;
 
    //명령 반복 횟수만큼 반복
    while (cmdCnt--) {
        if (cmd == 'L') {
            if (tmpDir == 0) {
                tmpDir = 3;
            }
            else {
                tmpDir--;
            }
        }
        else if (cmd == 'R') {
            if (tmpDir == 3) {
                tmpDir = 0;
            }
            else {
                tmpDir++;
            }
        }
        else {
            tmpi += di[tmpDir];
            tmpj += dj[tmpDir];
 
            //벽에 충돌한 경우
            if (tmpi <= 0 || tmpj <= 0 || tmpi > A || tmpj > B) {
                cout << "Robot " << id << " crashes into the wall" << "\n";
                return true;
            }
 
            //다른 로봇에 충돌한 경우
            if (map[tmpi][tmpj] != 0) {
                cout << "Robot " << id << " crashes into robot " << map[tmpi][tmpj] << "\n";
                return true;
            }
        }
    }
 
    //충돌이 발생하지 않았을 경우, 로봇위치 변경
    map[tmpi][tmpj] = id;
 
    robotInfo[id][0= tmpi;
    robotInfo[id][1= tmpj;
    robotInfo[id][2= tmpDir;
 
    return false;
}
 
int main() {
 
    //1.입력
    cin >> B >> A >> N >> M;
    for (int i = 1; i <= N; i++) {
        int tmpi, tmpj, tmpDir;
        char tmp;
 
        cin >> tmpj >> tmpi >> tmp;
 
        if (tmp == 'N') {
            tmpDir = 0;
        }
        else if (tmp == 'E') {
            tmpDir = 1;
        }
        else if (tmp == 'S') {
            tmpDir = 2;
        }
        else {
            tmpDir = 3;
        }
 
        //배열index 조정
        robotInfo[i][0= (A + 1- tmpi;
        robotInfo[i][1= tmpj;
        robotInfo[i][2= tmpDir;
 
        map[robotInfo[i][0]][robotInfo[i][1]] = i;
    }
    
    //2.명령 수행
    for (int i = 1; i <= M; i++) {
        int id, cmdCnt;
        char cmd;
        cin >> id >> cmd >> cmdCnt;
 
        if (!crash) {
            crash = simulation(id, cmd, cmdCnt);
        }
    }
 
    if (!crash) {
        cout << "OK" << "\n";
    }
 
    return 0;
}

 

시뮬레이션에 대해 알아볼 수 있는 문제였습니다.

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

 

3987번: 보이저 1호

문제 보이저 1호는 1977년에 발사된 NASA의 태양계 무인 탐사선이다. 현재 보이저 1호는 태양권덮개 (헬리오시스)에 있다. 보이저 1호와 같이 오랜 기간동안 활동하는 탐사선은 경로를 항성계를 만날 때 마다 라디오 시그널 메시지를 이용해서 기록하고 있다. 항성계를 N * M개의 직사각형으로 나누어져 있는 N행 M열의 직사각형 그리드라고 생각해보자. 각 칸은 행성, 블랙홀을 포함할 수 있으며, 비어있을 수도 있다. 탐사선은 인접한 네 칸(위, 아래, 오

www.acmicpc.net

<풀이>

1. 항성계를 입력 받고, 시작점부터 4방향(동,서,남,북) 차례차례 시그널을 보낸다.

2. 각 방향마다 시그널이 어떻게 가는지 구현하고, 결과를 저장한다.

3. 결과를 출력한다.

<해법>

1. 시그널이 무한히 전파될 수 있는 경우 구하는 방법.

=> 시그널이 '이전에 방문했던 좌표'를 '똑같은 방향'으로 방문했을 때, 시그널이 무한히 전파될 수 있는 경우가 됩니다.(코드의 visited배열을 사용)

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 <iostream>
#include <cstring>
using namespace std;
 
int di[] = { -1,0,1,0 };
int dj[] = { 0,1,0,-1 };
int changeDir_1[] = { 1,0,3,2 };
int changeDir_2[] = { 3,2,1,0 };
 
int N, M;
char map[502][502];
bool visited[502][502][4];
int si, sj;
 
char resDir[] = { 'U','R','D','L' };
int resTime = 0;
int resDirIndex;
bool isVoyager = false;
 
int main() {
 
    //입력
    cin >> N >> M;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            cin >> map[i][j];
        }
    }
    cin >> si >> sj;
 
    //시작점부터 4방향 탐색
    for (int i = 0; i < 4; i++) {
 
        //좌표, 방향, 시간, 방문 초기화
        memset(visited, falsesizeof(visited));
        int ni = si;
        int nj = sj;
        int dir = i;
        int time = 1;
        visited[si][sj][i] = true;
 
        while (true) {
 
            ni += di[dir];
            nj += dj[dir];
 
            //다음 시그널 위치가 범위를 넘어섰거나, 블랙홀을 만났을 경우 - 종료
            if (ni <= 0 || nj <= 0 || ni > N || nj > M || map[ni][nj] == 'C') {
                break;
            }
 
            //다음 시그널의 위치가 이전에 같은 방향으로 방문한적이 있을 경우 - 종료(Voyager)
            if (visited[ni][nj][dir]) {
                isVoyager = true;
                break;
            }
 
            //다음 시그널의 위치가 '.', '/', '\'일 경우 - 진행
            if (map[ni][nj] == '.') {
                
            }
            else if (map[ni][nj] == '/') {
                dir = changeDir_1[dir];
            }
            else {
                dir = changeDir_2[dir];
            }
            visited[ni][nj][dir] = true;
            time++;
        }
 
        //결과 저장
        if (isVoyager) {
            resDirIndex = i;
            break;
        }
        else {
            if (time > resTime) {
                resTime = time;
                resDirIndex = i;
            }
        }
    }
 
    //결과 출력
    if (isVoyager) {
        cout << resDir[resDirIndex] << "\n" << "Voyager";
    }
    else {
        cout << resDir[resDirIndex] << "\n" << resTime;
    }
}
 

 

시뮬레이션에 대해 알아볼 수 있는 문제였습니다.

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

 

2437번: 저울

하나의 양팔 저울을 이용하여 물건의 무게를 측정하려고 한다. 이 저울의 양 팔의 끝에는 물건이나 추를 올려놓는 접시가 달려 있고, 양팔의 길이는 같다. 또한, 저울의 한쪽에는 저울추들만 놓�

www.acmicpc.net

<풀이>

1. 저울추를 입력받는다.

2. 저울추를 정렬한다.

3. 결과값을 구하고 출력한다.

<해법>

1. 측정할 수 없는 무게를 구하는 방법.

=> 만약 내가 가지고 있는 저울추들로 1~K무게를 모두 만들 수 있다고 가정합니다. 다음 저울추(L)가 K무게 보다 같거나 작다면, 다음 저울추로 (1+L)~(K+L)무게를 모두 만들 수 있습니다. 그렇다면 다음 저울추까지 포함하면 1~(K+L)무게를 모두 만들 수 있는 것이 됩니다.

 

간단하게 예를 들면 가지고 있는 저울추들로 1,2,3,4 무게를 만들 수 있다고 해보겠습니다.

다음 저울추가 5라면 5,6,7,8,9 무게를 만들 수 있을 것이다. 따라서, 1~9 무게를 모두 만들 수 있습니다.

하지만, 다음 저울추가 6라면 6,7,8,9,10 무게를 만들 수 있게되어 무게 5는 측정을 하지 못합니다.

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
#include <iostream>
#include <algorithm>
using namespace std;
 
int N;
int arr[1000];
 
int main() {
 
    //입력
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> arr[i];
    }
 
    //정렬
    sort(arr, arr + N);
    
    //결과값 구하기
    int res = 1;
    for (int i = 0; i < N; i++) {
        if (arr[i] > res) {
            break;
        }
        res += arr[i];
    }
 
    //출력
    cout << res;
}

 

수학적인 사고에 대해 알아볼 수 있는 문제였습니다.

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

 

1992번: 쿼드트리

첫째 줄에는 영상의 크기를 나타내는 숫자 N 이 주어진다. N 은 언제나 2의 제곱수로 주어지며, 1≤N ≤64의 범위를 가진다. 두 번째 줄부터는 길이 N 의 문자열이 N 개 들어온다. 각 문자열은 0 또는 1의 숫자로 이루어져 있으며, 영상의 각 점들을 나타낸다.

www.acmicpc.net

<풀이>

1. 주어진 영상을 입력받는다.

2. 영상이 모두 0 또는 1로 되어있을 경우 값을 저장한다.

3. 모두 0 또는 1이 아닐경우 영상을 4개로 나눈다.

4. 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
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <vector>
using namespace std;
 
int N;
int map[64][64];
vector<char> res;
 
void go(int n, int x, int y) {
 
    //부분이 모두 0 또는 1 인지 검사
    bool allZero = true;
    bool allOne = true;
 
    for (int i = x; i < x + n; i++) {
        for (int j = y; j < y + n; j++) {
            if (map[i][j] == 1) {
                allZero = false;
            }
            else {
                allOne = false;
            }
        }
    }
 
    //종료조건
    if (allZero || allOne) {
        if (allZero) {
            res.push_back('0');
        }
        else {
            res.push_back('1');
        }
        return;
    }
 
    //4부분 탐색
    res.push_back('(');
    go(n / 2, x, y);
    go(n / 2, x, y + (n / 2));
    go(n / 2, x + (n / 2), y);
    go(n / 2, x + (n / 2), y + (n / 2));
    res.push_back(')');
 
    return;
}
 
int main() {
 
    //입력
    cin >> N;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            scanf_s("%1d"&map[i][j]);
        }
    }
 
    //go(변길이, 시작지점 i, 시작지점 j)
    go(N, 00);
 
    //출력
    for (int i = 0; i < res.size(); i++) {
        cout << res[i];
    }
}
 

 

재귀함수에 대해 알아볼 수 있는 문제였습니다.

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[C++] 백준 3987 - 보이저 1호  (0) 2020.04.30
[C++] 백준 2437 - 저울  (2) 2020.04.30
[C++] 백준 2436 - 공약수  (0) 2020.04.27
[C++] 백준 2559 - 수열  (0) 2020.04.27
[C++] 백준 2467 - 용액  (0) 2020.04.27

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

 

2436번: 공약수

첫째 줄에 두 개의 자연수가 빈칸을 사이에 두고 주어진다. 첫 번째 수는 어떤 두 개의 자연수의 최대공약수이고, 두 번째 수는 그 자연수들의 최소공배수이다. 입력되는 두 자연수는 2 이상 100,000,000 이하이다.

www.acmicpc.net

<풀이>

1. 최대공약수 최소공배수를 입력받고, 최소공배수를 최대공약수로 나눈 숫자를 구한다.

2. 그 숫자들의 약수 짝(두 약수를 곱해서 다시 그 숫자가 되는 두 수)을 구한다.

3. 약수 짝이 서로소인 것을 구한다.

4. 약수 짝(두 수)에 각각 최대공약수를 곱하면, 정답이 가능한 두 수가 된다.

5. 가능한 두 수 중 합이 가장 작은 것을 출력한다.

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
#include <iostream>
#include <cmath>
using namespace std;
 
long long int gcd, lcm, tmp;
long long int resA = 0, resB = 0;
 
int main() {
 
    //입력
    cin >> gcd >> lcm;
 
    //tmp = 최소공배수 / 최대공약수
    tmp = lcm / gcd;
 
    //tmp의 약수 두 수 구하고, 서로소인지 판별
    for (long long int i = 1; i * i <= tmp; i++) {
        if (tmp % i == 0) {
            //약수 두 수
            long long int tmpA = i;
            long long int tmpB = tmp / i;
 
            //약수 두 수가 서로소인지 판별
            bool isAble = true;
            long long int cnt = 0;
            for (long long int j = 1; j <= tmpA; j++) {
                if (tmpA % j == 0 && tmpB % j == 0) {
                    cnt++;
                }
                //약수의 개수가 1개보다 많은 경우 불가능
                if (cnt > 1) {
                    isAble = false;
                    break;
                }
            }
 
            //가능한 경우 중 가장 마지막에 저장되는 값이 합이 가장 작게된다
            if (isAble) {
                resA = tmpA * gcd;
                resB = tmpB * gcd;
            }
        }
    }
 
    //출력
    cout << resA << " " << resB << "\n";
}
 

 

최대공약수, 최소공배수에 대해 알아볼 수 있는 문제였습니다.

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[C++] 백준 2437 - 저울  (2) 2020.04.30
[C++] 백준 1992 - 쿼드트리  (0) 2020.04.27
[C++] 백준 2559 - 수열  (0) 2020.04.27
[C++] 백준 2467 - 용액  (0) 2020.04.27
[C++] 백준 2468 - 안전 영역  (0) 2020.04.27

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

 

2559번: 수열

첫째 줄에는 두 개의 정수 N과 K가 한 개의 공백을 사이에 두고 순서대로 주어진다. 첫 번째 정수 N은 온도를 측정한 전체 날짜의 수이다. N은 2 이상 100,000 이하이다. 두 번째 정수 K는 합을 구하기 위한 연속적인 날짜의 수이다. K는 1과 N 사이의 정수이다. 둘째 줄에는 매일 측정한 온도를 나타내는 N개의 정수가 빈칸을 사이에 두고 주어진다. 이 수들은 모두 -100 이상 100 이하이다. 

www.acmicpc.net

<풀이>

1. 숫자 N개 중 K개의 합을 처음부터 끝까지 모두 확인해보고 최댓값을 갱신한다.

<해법>

1. 배열 처음~끝까지 K개의 합을 비교하는 방법

=> 투 포인터 활용한다. 부분집합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
#include <iostream>
using namespace std;
 
const int MAX = 100000 + 1;
 
int arr[MAX];
long long int res;
 
int main() {
 
    int N, K;
 
    //입력
    cin >> N >> K;
 
    for (int i = 0; i < N; i++) {
        cin >> arr[i];
    }
 
    //초기 배열1 설정
    int s = 0;
    long long int sum = 0;
    for (int i = s; i < s+K; i++) {
        sum += arr[i];
    }
    res = sum;
 
    //배열 탐색
    while (true) {
        //기존 합에 앞에 부분을 뺀다
        sum -= arr[s];
        //배열의 범위를 벗어날 경우
        if (s+>= N) {
            break;
        }
        //합에 뒷부분을 더한다
        sum += arr[s+K];
        if (sum > res) {
            res = sum;
        }
        //다음 배열 index
        s++;
    }
 
    //출력
    cout << res;
}

 

투포인터에 대해 알아볼 수 있는 문제였습니다.

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[C++] 백준 1992 - 쿼드트리  (0) 2020.04.27
[C++] 백준 2436 - 공약수  (0) 2020.04.27
[C++] 백준 2467 - 용액  (0) 2020.04.27
[C++] 백준 2468 - 안전 영역  (0) 2020.04.27
[C++] 백준 2469 - 사다리 타기  (0) 2020.04.27

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

 

2467번: 용액

첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하의 정수이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 오름차순으로 입력되며, 이 수들은 모두 -1,000,000,000 이상 1,000,000,000 이하이다. N개의 용액들의 특성값은 모두 서로 다르고, 산성 용액만으로나 알칼리성 용액만으로 입력이 주어지는 경우도 있을 수 있다.

www.acmicpc.net

<풀이>

1. 용액 배열 양쪽에서 다가오면서, 두 개의 합이 0에 가장 가까울 경우 저장해준다.

2. 두 개의 합이 0 보다 작을 경우, 조금 더 0보다 가까운 경우가 있는지 알아보기 위해 왼쪽에서 다가오는 index를 하나 늘려준다.

3. 두 개의 합이 0보다 클 경우, 조금 더 0보다 가까운 경우가 있는지 알아보기 위해 오른쪽에서 다가오는 index를 하나 줄여준다.

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
#include <iostream>
using namespace std;
 
const int MAX = 100000 + 1;
 
int N;
long long int arr[MAX];
long long int resA, resB;
 
int main() {
 
    //입력
    cin >> N;
    for (int i = 0; i < N; i++) {
        cin >> arr[i];
    }
 
    //양쪽에서 다가오는 index의 초기값
    int left = 0;
    int right = N - 1;
 
    //결과값의 초기값
    int resLiquid = abs(arr[left] + arr[right]);
    resA = arr[left];
    resB = arr[right];
 
    //양쪽에서 다가오는 반복, 모든 경우 탐색 완료시 종료
    while (left < right) {
        int tmpLiquid = arr[left] + arr[right];
        if (abs(tmpLiquid) < resLiquid) {
            resLiquid = abs(tmpLiquid);
            resA = arr[left];
            resB = arr[right];
        }
 
        if (tmpLiquid < 0) {
            left++;
        }
        else {
            right--;
        }
    }
 
    //출력
    cout << resA << " " << resB << "\n";
}
 

 

투포인터에 대해 알아볼 수 있는 문제였습니다.

'알고리즘 문제풀이 > 백준' 카테고리의 다른 글

[C++] 백준 1992 - 쿼드트리  (0) 2020.04.27
[C++] 백준 2436 - 공약수  (0) 2020.04.27
[C++] 백준 2559 - 수열  (0) 2020.04.27
[C++] 백준 2468 - 안전 영역  (0) 2020.04.27
[C++] 백준 2469 - 사다리 타기  (0) 2020.04.27

+ Recent posts