https://www.swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV5V4A46AdIDFAWu&categoryId=AV5V4A46AdIDFAWu&categoryType=CODE

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

www.swexpertacademy.com

먼저 두 일꾼이 선택하는 벌통이 겹치지 않도록 모든 경우를 구해준다.

두 일꾼이 벌통을 가로로 m개씩 벌통을 선택했다면 각각의 벌통에서 c를 넘지 않도록 벌꿀을 채취해서 얻는 수익의 최댓값을 구해주면 된다. 문제에 나와있듯이 수익은 벌꿀의 양을 제곱해서 더해준 값이다.

 

 

자세한 설명은 코드 참고! result 변수 대신에 수익을 나타내는 단어로 사용했으면 더 좋았을 것 같다...

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
#include <iostream>
#include <vector>
using namespace std;
 
int ans;
int n, m, c;
int honey[10][10];
bool check[10][10];
vector<int> workers[2];
 
 
//선택한 벌통에서 벌꿀을 채취한다. (sum은 벌통에 들은 꿀의 양, result는 꿀의 양을 제곱해서 얻는 수익)
int selectHoney(int worker,int index, int sum, int result) {
    //채취할 수 있는 최대 양 c를 넘어간 경우 불가능하므로 -1을 리턴
    if (sum > c) return -1;
 
    //m개의 벌통에 대해서 꿀을 채취할지 말지 모두 정해졌다.
    if (index == m) {
        return result;
    }
 
    //제곱한 결과
    int newresult = workers[worker][index] * workers[worker][index];
 
    int rt = 0;
 
    //index번째 벌통에서 꿀을 채취하는 경우
    int tmp1 = selectHoney(worker, index + 1, sum + workers[worker][index], result+newresult);
 
    //index번째 벌통에서 꿀을 채취하지 않는 경우
    int tmp2 = selectHoney(worker, index + 1, sum, result);
    
    //위 의 두가지 경우가 -1이 아니라면 더 작은 값을 리턴해준다.
    if (tmp1 != -1) rt = tmp1;
    if (tmp2 != -1 && rt < tmp2) rt = tmp2;
 
    return rt;
}
 
 
//두명의 일꾼이 벌통을 가로로 m개만큼 선택한다.
void solve(int index, int x) {
    //일꾼 두명 모두 선택이 끝났다.
    if (index > 1) {
        //두 일꾼의 벌통에서 꿀을 채취한다.
        int tmp = selectHoney(0000+ selectHoney(1000);
        if(ans < tmp) ans = tmp;
        return;
    }
 
    for (int i = x; i < n; i++) {
        for (int j = 0; j <= n-m; j++) {
            
            //현재 칸으로부터 m개의 벌통을 선택할 수 있는지 검사
            bool flag = true;
            for (int k = j; k < j + m; k++) {
                if (check[i][k]) {
                    flag = false;
                    break;
                }
            }
            //선택할 수 없다면 다음 칸으로 넘어간다.
            if (!flag) continue;
 
 
            //m개의 칸에 선택 표시를 하고 index번째 일꾼의 벌통에 넣는다.
            for (int k = j; k < j + m; k++) {
                check[i][k] = true;
                workers[index].push_back(honey[i][k]);
                
            }
 
            //다음일꾼의 경우를 탐색
            solve(index+1, i);
 
 
            //위의 탐색에서 돌아왔다면 다음 경우를 위해서 표시를 false로 바꿔주고 벡터에서도 빼준다.
            for (int k = j; k < j + m; k++) {
                check[i][k] = false;
                workers[index].pop_back();
            }
 
        }
    }
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    int T;
    cin >> T;
    for (int tc = 1; tc <= T; tc++) {
        ans = 0;
        cin >> n >> m >> c;
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                cin >> honey[i][j];
                check[i][j] = false;
            }
        }
 
        solve(0,0);
 
        cout << '#' << tc << ' ' << ans << '\n';
    }
 
    return 0;
}
Colored by Color Scripter
 

https://www.swexpertacademy.com/main/code/problem/problemDetail.do?contestProbId=AV5PpFQaAQMDFAUq&categoryId=AV5PpFQaAQMDFAUq&categoryType=CODE

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

www.swexpertacademy.com

1월부터 12월까지 모든 달에 대해서 1일권을 사용할지/1달권을 사용할지/3달권을 사용할지/ 1년권을 사용할지를

모두 해보면 되는데 1년권을 사용할 경우에는 한번에 12개월치를 다 정해주는거나 마찬가지이므로 정답에 미리 넣어놓고 다른 경우들과 비교해주면된다.

 

1일권을 이용하는 경우에는

해당 달의 이용일수X1월권 비용을 전체 비용에 더해준다.

 

1달권을 이용하는 경우에는 

해당 달에 몇번을 이용하든 상관없으므로 1달권의 비용만 더해주면 된다.

 

3달권을 이용하는 경우에는

마찬가지로 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
#include <iostream>
using namespace std;
 
int price[4];
int plan[13];
int ans;
 
void solve(int month, int sum) {
    if (month > 12) {
        if (ans > sum) ans = sum;
        return;
    }
 
 
    //이번 달에 수영장을 이용하지 않는 경우
    if (plan[month] == 0) {
        solve(month + 1, sum);
    }
    else {
 
        //1일 이용권을 사용하는 경우
        solve(month + 1, plan[month] * price[0+ sum);
        
        //1달 이용권을 사용하는 경우
        solve(month + 1, price[1+ sum);
 
        //3달 이용권을 사용하는 경우
        solve(month + 3, price[2+ sum);
 
    }
 
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    int T;
    cin >> T;
    for (int tc = 1; tc <= T; tc++) {
 
        for (int i = 0; i < 4; i++) {
            cin >> price[i];
        }
 
        for (int j = 1; j <= 12; j++) {
            cin >> plan[j];
        }
 
        ans = price[3];
        solve(10);
 
        cout << '#' << tc << ' ' << ans << '\n';
    }
 
    return 0;
}
Colored by Color Scripter
 

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

 

14500번: 테트로미노

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변끼리 연결되어 있어야 한다. 즉, 꼭짓점과 꼭짓점만 맞닿아 있으면 안 된다. 정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 다음과 같은 5가지가 있다. 아름이는 크기가 N×M인 종이 위에 테트로미노 하나를 놓으려고 한다. 종이는 1×1 크기의 칸으로 나누

www.acmicpc.net

테트로미노는 (ㅏ,ㅓ,ㅗ,ㅜ)를 제외하고 시작점으로부터 인접한 칸으로 세 번 더 이동한 모양이다.

예를 들어 밑의 그림에서 대충 저 1을 시작점으로 잡았다고 쳤을 때 아무렇게나 인접한 칸으로 네 번 이동하면

테트로미노가 된다. 즉, 모든 경우를 해보면 (ㅏ,ㅓ,ㅗ,ㅜ)를 제외한 모든 테트로미노를 만들 수 있다.

 

           
  1 2      
    3      
    4      
           
           
           
           
  1 2      
    3 4    
           
           
           

 

모든 칸에서부터 시작하여 모든 종류의 테트로미노를 만들어주면 되는데

(ㅏ,ㅓ,ㅗ,ㅜ)는 밑에 코드처럼 따로 일일이 처리해주어야 한다.

ㅡ 에서 위아래로 튀어나온 부분(?)이랑 ㅣ 에서 좌우로 튀어나온 부분을 i, j로 놓고 그 나머지 부분을 추가적으로 더해줬다. 얘네도 범위를 벗어나지 않는다면 해당 네 칸을 더해준 다음에 최댓값과 각각 비교해주면 된다.

 

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
#include <iostream>
using namespace std;
 
 
int n, m;
int ans;
int map[500][500];
bool check[500][500];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
void solve(int x, int y, int sum, int cnt) {
    //테트로미노가 됐다!
    if (cnt == 4) {
        //최댓값을 비교하여 저장
        if (ans < sum) ans = sum;
        return;
    }
 
    for (int k = 0; k < 4; k++) {
        int nx = x + dx[k];
        int ny = y + dy[k];
 
        //범위 체크
        if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
 
        //방문하지 않았다면 방문
        if (!check[nx][ny]) {
            check[nx][ny] = true;
            //sum에 현재 칸의 값을 더하여 넘겨준다. 현재칸을 선택했으므로 cnt도 증가
            solve(nx, ny, sum + map[nx][ny], cnt + 1);
            check[nx][ny] = false;
        }
    }
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> map[i][j];
        }
    }
 
 
    //모든 칸에서 모든 테트로미노를 놓는 경우를 모두 해본다.
    int sum;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            check[i][j] = true;
            solve(i, j, map[i][j], 1);
            check[i][j] = false;
 
 
            //ㅏ
            if (i - 1 >= 0 && i + 1 < n && j + 1 < m) {
                sum = map[i][j];
                sum += map[i - 1][j + 1+ map[i][j + 1+ map[i + 1][j + 1];
                if (ans < sum) ans = sum;
            }
 
 
            //ㅗ
            if (i - 1 >= 0 && j - 1 >= 0 && j + 1 < m) {
                sum = map[i][j];
                sum += map[i - 1][j - 1+ map[i - 1][j] + map[i - 1][j + 1];
                if (ans < sum) ans = sum;
            }
 
 
            //ㅓ
            if (i - 1 >= 0 && i + 1 < n && j - 1 >= 0) {
                sum = map[i][j];
                sum += map[i - 1][j - 1+ map[i][j - 1+ map[i + 1][j - 1];
                if (ans < sum) ans = sum;
            }
 
 
            //ㅜ
            if (i + 1 < n && j - 1 >= 0 && j + 1 < m) {
                sum = map[i][j];
                sum += map[i + 1][j - 1+ map[i + 1][j] + map[i + 1][j + 1];
                if (ans < sum) ans = sum;
            }
 
        }
    }
 
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 3190. 뱀  (0) 2019.08.06
[BOJ] 15683. 감시  (0) 2019.08.05
[BOJ] 14499. 주사위 굴리기  (0) 2019.08.04
[BOJ] 14503. 로봇 청소기  (0) 2019.08.04
[BOJ] 14502. 연구소  (0) 2019.08.04

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

 

14499번: 주사위 굴리기

첫째 줄에 지도의 세로 크기 N, 가로 크기 M (1 ≤ N, M ≤ 20), 주사위를 놓은 곳의 좌표 x y(0 ≤ x ≤ N-1, 0 ≤ y ≤ M-1), 그리고 명령의 개수 K (1 ≤ K ≤ 1,000)가 주어진다. 둘째 줄부터 N개의 줄에 지도에 쓰여 있는 수가 북쪽부터 남쪽으로, 각 줄은 서쪽부터 동쪽 순서대로 주어진다. 주사위를 놓은 칸에 쓰여 있는 수는 항상 0이다. 지도의 각 칸에 쓰여 있는 수는 10을 넘지 않는 자연수 또는 0이다. 마

www.acmicpc.net

주사위

주사위 배열을 만들어놓고 위의 그림에 나와 있는 숫자를 배열의 인덱스로 사용하였다.

즉 dice[0] 이 윗면, dice[5]가 바닥면이다.

주사위를 굴릴 때마다 배열의 값을 주사위의 해당 면에 오는 숫자로 바꿔주면 된다.

쉽게 생각하면 주사위는 그대로 있고 주사위 위의 숫자가 굴리는 방향에 따라 움직인다고 생각하면 된다.

 

굴릴 방향에 따라서 어떻게 변하는지는 그냥 일일이 해주면 되는데

예를 들어 d가 1일 경우(동쪽으로 주사위를 굴리는 경우)

위의 그림에서 3번, 0번, 2번, 5번 면의 숫자가 바뀐다. 모두 동쪽으로 한 칸씩 움직이기 때문에

0번은 2번으로, 2번은 바닥면인 5번으로, 5번에 에 있던 숫자는 3번으로, 3번에 있던 숫자는 윗면인 0으로 올라오게 된다.

이런 식으로 네 방향에 대해서 하나하나 구해주면 된다.

 

주의할 점은 범위를 벗어나는 경우에 해당 명령을 무시해야 하므로 주사위가 이동하지 않도록 해야 한다.

밑의 코드에서는 nx, ny 변수를 새로 만들어서 범위를 검사해주고 범위를 벗어나지 않으면 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
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
#include <iostream>
using namespace std;
 
int dx[] = { 0,0,0,-1,1 };
int dy[] = { 0,1,-1,0,0 };
 
int map[20][20];
int dice[6];
 
int n, m, x, y, k;
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
 
    cin >> n >> m >> x >> y >> k;
 
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> map[i][j];
        }
    }
 
    int dir;
    int nx, ny;
    while (k-- > 0) {
        cin >> dir;
 
        nx = x + dx[dir];
        ny = y + dy[dir];
 
        //범위를 벗어나면 명령을 무시
        if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
 
 
        if (dir == 1) {
            int tmp = dice[0];
            dice[0= dice[3];
            dice[3= dice[5];
            dice[5= dice[2];
            dice[2= tmp;
        }
        else if (dir == 2) {
            int tmp = dice[0];
            dice[0= dice[2];
            dice[2= dice[5];
            dice[5= dice[3];
            dice[3= tmp;
        }
        else if (dir == 3) {
            int tmp = dice[0];
            dice[0= dice[4];
            dice[4= dice[5];
            dice[5= dice[1];
            dice[1= tmp;
        }
        else if (dir == 4) {
            int tmp = dice[0];
            dice[0= dice[1];
            dice[1= dice[5];
            dice[5= dice[4];
            dice[4= tmp;
        }
 
 
        //지도의 칸이 0이면 주사위의 바닥면이 지도에 복사
        if (map[nx][ny] == 0) {
            map[nx][ny] = dice[5];
        }
        else {
            //그렇지 않다면 칸에 쓰여져 있는 수가 주사위 바닥면으로 복사 후에 0으로 변경
            dice[5= map[nx][ny];
            map[nx][ny] = 0;
        }
 
 
        x = nx;
        y = ny;
 
 
        //윗면을 출력
        cout << dice[0<< '\n';
    }
 
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 15683. 감시  (0) 2019.08.05
[BOJ] 14500. 테트로미노  (0) 2019.08.04
[BOJ] 14503. 로봇 청소기  (0) 2019.08.04
[BOJ] 14502. 연구소  (0) 2019.08.04
[BOJ] 15685. 드래곤 커브  (0) 2019.08.04

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

 

14503번: 로봇 청소기

로봇 청소기가 주어졌을 때, 청소하는 영역의 개수를 구하는 프로그램을 작성하시오. 로봇 청소기가 있는 장소는 N×M 크기의 직사각형으로 나타낼 수 있으며, 1×1크기의 정사각형 칸으로 나누어져 있다. 각각의 칸은 벽 또는 빈 칸이다. 청소기는 바라보는 방향이 있으며, 이 방향은 동, 서, 남, 북중 하나이다. 지도의 각 칸은 (r, c)로 나타낼 수 있고, r은 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로 부터 떨어진 칸의 개수이다. 로봇 청소기는 다음

www.acmicpc.net

문제에 나와있는 대로 차근차근 구현해주면 되는 시뮬레이션 문제이다.

이 문제에는 방향 전환만 주의해주면 쉽게 해결할 수 있다.

 

처음에 벽이랑 범위 벗어난 거랑 구분해야 하는 줄 알고 고생할뻔했는데 문제를 자세히 보면 모든 외곽은 벽으로 되어있기 때문에 범위 체크를 따로 해주지 않아도 된다.

 

문제에서 0인 경우에는 북쪽/ 1인 경우에는 동쪽/ 2인 경우에는 남쪽/ 3인 경우에는 서쪽이라고 주어졌으므로

그에 맞게 다음과 같이 방향 배열을 잘 저장해놓는다.

 

dx[] = { -1,0,1,0 }

dy[] = { 0,1,0,-1 }

 

청소를 하기 위해 왼쪽 방향을 보기 위해서는 (현재 방향 + 3)%4를 해주면 왼쪽 방향 index로 올 수 있다.

즉, 현재 동쪽(1)을 바라보고 있는 상황에서는 왼쪽 방향은 북쪽(0)인데 (1+3)%4 == 0으로 북쪽을 바라보게 된다.

 

후진을 하는 경우에는 북쪽을 바라보고 있었다면 남쪽 방향으로, 남쪽을 바라보고 있었다면 북쪽으로 이동하면 된다.

마찬가지로 동에서 서, 서에서 동으로 이동해주면 된다.

즉, 0에서 2, 2에서 0, 1에서 3, 3에서 1 이므로 위와 비슷하게 (현재 방향+2)%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
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
using namespace std;
 
int dx[] = { -1,0,1,0 };
int dy[] = { 0,1,0,-1 };
int n, m;
int map[50][50];
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n >> m;
 
    int r, c, dir;
    cin >> r >> c >> dir;
 
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> map[i][j];
        }
    }
 
    int cnt = 0;
    while (true) {
        //현재 위치 청소
        if (map[r][c] == 0) {
            map[r][c] = 2;
            cnt++;
        }
 
 
        //네 방향 모두 청소가 이미 되어있거나 벽인 경우
        if (map[r - 1][c] != 0 && map[r + 1][c] != 0 && map[r][c - 1!= 0 && map[r][c + 1!= 0) {
            
            //후진
            r += dx[(dir + 2) % 4];
            c += dy[(dir + 2) % 4];
 
            //뒤쪽 방향이 벽이라 후진도 할 수 없는 경우
            if (map[r][c] == 1) {
                //작동 중지
                break;
            }
            else {
                continue;
            }
        }
 
 
        //현재 방향의 왼쪽 방향
        dir = (dir + 3) % 4;
 
        //왼쪽 방향에 청소하지 않은 공간이 존재한다.
        if (map[r + dx[dir]][c + dy[dir]] == 0) {
            //해당방향으로 전진
            r += dx[dir];
            c += dy[dir];
        }
        else {
            continue;
        }
 
 
    }
 
    cout << cnt << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 14500. 테트로미노  (0) 2019.08.04
[BOJ] 14499. 주사위 굴리기  (0) 2019.08.04
[BOJ] 14502. 연구소  (0) 2019.08.04
[BOJ] 15685. 드래곤 커브  (0) 2019.08.04
[BOJ] 15686. 치킨 배달  (0) 2019.08.04

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

 

14502번: 연구소

인체에 치명적인 바이러스를 연구하던 연구소에서 바이러스가 유출되었다. 다행히 바이러스는 아직 퍼지지 않았고, 바이러스의 확산을 막기 위해서 연구소에 벽을 세우려고 한다. 연구소는 크기가 N×M인 직사각형으로 나타낼 수 있으며, 직사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는 빈 칸, 벽으로 이루어져 있으며, 벽은 칸 하나를 가득 차지한다.  일부 칸은 바이러스가 존재하며, 이 바이러스는 상하좌우로 인접한 빈 칸으로 모두 퍼져나갈 수 있다.

www.acmicpc.net

n과 m이 최대 8이고 벽은 딱 3개만 세우면 되므로 시간 안에 완전 탐색을 해서 해결할 수 있다.

 

빈칸에 벽 3개를 세우는 모든 경우를 구해주고 3개를 정할 때마다 BFS로 바이러스를 퍼뜨려서 최대 안전영역의 수를 구해주면 된다. 즉 전형적인 완전 탐색 + BFS 문제이다.

 

안전 영역의 수는 0인 곳의 수를 미리 cnt 변수에 저장해 두고 바이러스가 퍼질 때마다 cnt에서 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
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
 
int n, m, ans;
int map[9][9];
bool check[9][9];
 
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
 
int bfs() {
    queue<pair<intint>> q;
    memset(check, falsesizeof(check));
 
    //안전역역의 수
    int cnt = 0;
 
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            //안전영역이면 cnt증가
            if (map[i][j] == 0) {
                cnt++;
            } else if (map[i][j] == 2) {
                //바이러스이면 큐에 넣어준다.
                q.push(make_pair(i, j));
                check[i][j] = true;
            }
        }
    }
 
 
    int x, y;
    //바이러스를 퍼뜨린다.
    while (!q.empty()) {
        x = q.front().first;
        y = q.front().second;
        q.pop();
 
        for (int k = 0; k < 4; k++) {
            int nx = x + dx[k];
            int ny = y + dy[k];
 
            //범위 체크
            if (nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
            //벽
            if (map[nx][ny] == 1continue;
            //방문 체크
            if (check[nx][ny]) continue;
 
 
            //(nx,ny)로 바이러스 확산
            q.push(make_pair(nx, ny));
            check[nx][ny] = true;
 
            //안전영역의 수가 감소한다.
            cnt--;
        }
 
    }
 
    //안전영역의 수를 리턴
    return cnt;
}
 
void solve(int cnt) {
    //벽 3개를 세웠다.
    if (cnt == 3) {
        int tmp = bfs();
 
        //안전영역의 최댓값을 저장
        if (ans < tmp) ans = tmp;
        return;
    }
 
 
    //벽을 세우는 모든 경우를 구해준다.
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
 
            // (i,j)가 빈칸이면 벽을 세워준다.
            if (map[i][j] == 0) {
                map[i][j] = 1;
                solve(cnt + 1);
                map[i][j] = 0;
            }
 
 
        }
    }
 
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n >> m;
 
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> map[i][j];
        }
    }
 
 
    solve(0);
 
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 14499. 주사위 굴리기  (0) 2019.08.04
[BOJ] 14503. 로봇 청소기  (0) 2019.08.04
[BOJ] 15685. 드래곤 커브  (0) 2019.08.04
[BOJ] 15686. 치킨 배달  (0) 2019.08.04
[BOJ] 16234. 인구 이동  (0) 2019.08.02

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

 

15684번: 사다리 조작

사다리 게임은 N개의 세로선과 M개의 가로선으로 이루어져 있다. 인접한 세로선 사이에는 가로선을 놓을 수 있는데, 각각의 세로선마다 가로선을 놓을 수 있는 위치의 개수는 H이고, 모든 세로선이 같은 위치를 갖는다. 아래 그림은 N = 5, H = 6 인 경우의 그림이고, 가로선은 없다. 초록선은 세로선을 나타내고, 초록선과 점선이 교차하는 점은 가로선을 놓을 수 있는 점이다. 가로선은 인접한 두 세로선을 연결해야 한다. 단, 두 가로선이 연속하거나 서로

www.acmicpc.net

사실 구현자체는 어렵지 않았는데 시간초과가 계속 나서 한참 고생했던 문제이다...

먼저 문제에서 3개 넘게 사용해야하는 경우에는 -1을 출력하라고 했으므로

가로선을 놓는 모든 경우를 탐색해줄 수 있긴하지만 백트래킹을 잘해줘야한다...ㅠㅠ

 

 

내 기준 이 문제에서 중요한 부분!

1. 가로선을 놓을 때 나는 가로선의 시작점을 1로 놓고 끝점을 2(빈 곳은 0)로 놓았다.

단순히 true/false로 놓으면 왼쪽으로 가는지 오른쪽으로 가는지를 다시 알아내야하기 때문이다.

이 아이디어는 사실 다른 곳에서 본거다ㅎㅎ

 

2. 사다리 타기를 시작할 때, 왼쪽이나 오른쪽으로 열을 이동하는 경우에도 한칸을 내려가도록 해줘야한다.

그렇지 않다면 가로선에서 계속 오른쪽 왼쪽을 왔다갔다하는 무한 루프에 빠질 수 있다.

 

3. 시간 초과를 해결하기 위한 가장 중요한 부분인데, 가로선을 놓을 곳을 탐색할 때 다시 (1,1)부터 하면 안되고 현재 가로선을 놓아준 행부터 다시 탐색을 시작하도록 현재 행을 매개변수로 넘겨준다.

 

4. 마지막으로 개인적으로 실수했던 부분인데 1개만 놓는 경우, 2개를 놓는 경우, 3개를 놓는 경우를 각각 해줬었다.

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
#include <iostream>
#include <vector>
using namespace std;
 
int n, m, h;
int check[32][11];
 
bool ispossible() {
 
    //모든 세로선에 대해서 검사
    for (int i = 1; i <= n; i++) {
        int x = 0;
        int y = i;
 
        //사다리 타기 시작
        while (true) {
            //맨 밑에 도착
            if (x == h + 1) {
                //i번의 결과가 i번이면 break후 에 다음 세로선을 검사
                //그렇지 않은 경우에는 바로 false를 리턴
                if (y == i) break;
                else return false;
            }
 
 
            //1인 경우에는 오른쪽사다리로 이동
            if (check[x + 1][y] == 1) {
                y++;
            }//2인 경우는 왼쪽사다리로 이동
            else if (check[x + 1][y] == 2) {
                y--;
            }
 
            //한칸 내려간다.
            x++;
        }
 
    }
 
 
    return true;
 
}
 
int select(int x, int index) {
 
    //3을 넘어가면 바로 -1을 리턴
    if (index > 3) {
        return -1;
    }
 
    int ans = -1;
 
 
    //x행부터 탐색을 시작
    for (int i = x; i <= h; i++) {
        for (int j = 1; j < n; j++) {
            if (check[i][j] != 0continue;
            if (check[i][j + 1!= 0continue;
 
            //i행의 j번과 j+1번에 가로선을 놓는다.
            check[i][j] = 1;
            check[i][j + 1= 2;
 
 
            //가로선을 놓은 상태에서 사다리 게임의 결과를 바로 확인
            if (ispossible()) {
                //i번 세로선이 i번에 도착한다면 현재 index(사용한 가로선 수)를 ans에 저장
                ans = index;
            }
 
            //현재 가로선을 놓은 상태에서 추가로 다른 곳에 하나 더 놓는 경우를 탐색
            //다음 탐색에서는 i행부터 진행한다.
            int tmp = select(i, index + 1);
 
            //ans가 초기값이라면 다음 탐색에서 받아온 tmp를 저장
            if (ans == -1) ans = tmp;
 
            //다른 곳에 가로선을 놓는 경우를 위해서 다시 가로선을 없애준다.
            check[i][j] = 0;
            check[i][j + 1= 0;
        }
    }
 
    return ans;
 
}
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    cin >> n >> m >> h;
    int a, b;
 
    //가로선의 왼쪽점을 1, 오른쪽점을 2로 놓는다.
    for (int i = 0; i < m; i++) {
        cin >> a >> b;
        check[a][b] = 1;
        check[a][b + 1= 2;
    }
 
    //가로선을 추가로 놓지않고 가능하다면 바로 0을 출력
    if (ispossible()) cout << 0 << '\n';
    else {
 
        cout << select(11<< '\n';
    }
 
 
 
    return 0;
}
Colored by Color Scripter
 

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

 

15685번: 드래곤 커브

첫째 줄에 드래곤 커브의 개수 N(1 ≤ N ≤ 20)이 주어진다. 둘째 줄부터 N개의 줄에는 드래곤 커브의 정보가 주어진다. 드래곤 커브의 정보는 네 정수 x, y, d, g로 이루어져 있다. x와 y는 드래곤 커브의 시작 점, d는 시작 방향, g는 세대이다. (0 ≤ x, y ≤ 100, 0 ≤ d ≤ 3, 0 ≤ g ≤ 10) 입력으로 주어지는 드래곤 커브는 격자 밖으로 벗어나지 않는다. 드래곤 커브는 서로 겹칠 수 있다. 방향은 0, 1, 2,

www.acmicpc.net

다들 쉽다고 하는 문제인데 나는 개인적으로 쉽지는 않았다. 실제로 규칙만 금방 알아내면 어렵지는 않다.

하지만 나는 일단 x좌표가 열이고 y좌표가 행이라 처음에 너무 헷갈렸었다....

일반적인 수학에서는 원래 그렇긴하지만 코딩할 때는 보통 r,c이면 r이 행이니까...ㅠ

그리고 처음에 규칙을 좌표에서 찾느라 헤매기도 했다...

 

 

이 문제는 세대별로 방향에 규칙이 있다.

문제의 예시를 봐보자.

0세대 드래곤 커브의 방향이 0이었다면, 1세대에 새로 생기는 방향은 1이다.

그리고 다음 2세대에 새로 생기는 방향은 순서대로 2, 1 이다.

그리고 다시 3세대에는 2, 3, 2, 1 방향이 새로 생긴다.

 

정리해서 보면 다음과 같다.

0세대 : 0

1세대 : 0 1

2세대 : 0 1 2 1

3세대 : 0 1 2 1 2 3 2 1 

 

 

3세대로 예를 들어보면 다음과 같이 방향이 1씩 증가했음을 알 수 있다.

화살표가 이어진 점은 화살표의 출발점이 이전 세대의 원래 점이고

화살표가 가르키는 점이 90도 회전해서 새로 생기는 점이다.

즉, 이전에 있었던 점의 방향으로부터 1이 더해졌음을 알 수 있다.

 

1세대와 2세대도 다시 보면 같은 규칙을 가지고 있는 것을 알 수 있다.

위의 규칙을 이용해서 방향 index를 문제에서 주어진 대로 dx와 dy배열에 잘 저장해서 구현해주면 된다.

 

 

드래곤 커브를 구했으면 이제 각 칸의 정사각형의 네 꼭짓점이 드래곤 커브에 포함되는지를 구해줘야한다.

드래곤 커브끼리 겹치는 부분이 있을 수도 있으므로 드래곤 커브를 먼저 모두 구한 뒤에 나중에 한번에 계산해준다.

 

(0,0)부터 시작하여 시작점으로부터 오른쪽, 아래, 오른쪽아래가 드래곤 커브에 포함되어 있는지 검사해주면 된다.

맨 마지막행이나 열에서 범위를 넘어가지 않도록 99번째 행과 99번째 열까지만 검사를 진행한다.

 

 

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
#include <iostream>
#include <vector>
using namespace std;
 
bool check[101][101];
int dx[] = { 1,0,-1,0 };
int dy[] = { 0,-1,0,1 };
int x, y, d, g;
vector<int> dir;
 
 
int calc() {
    int cnt = 0;
    for (int i = 0; i < 100; i++) {
        for (int j = 0; j < 100; j++) {
            //네 꼭짓점을 검사
            if (!check[i][j]) continue;
            if (!check[i + 1][j]) continue;
            if (!check[i][j + 1]) continue;
            if (!check[i + 1][j + 1]) continue;
 
            //모두 포함되어 있다면 cnt 증가
            cnt++;
        }
    }
 
    return cnt;
}
 
 
 
void solve() {
    //0세대 처리
    check[y][x] = true;
    x += dx[d];
    y += dy[d];
    check[y][x] = true;
    dir.push_back(d);
 
 
    int dsize;
    //구해줄 세대 수만큼 진행
    for (int i = 0; i < g; i++) {
        dsize = dir.size();
 
        //회전하여서 이번 세대에서 새로 생길 방향들을 추가해준다.
        for (int j = dsize - 1; j >= 0; j--) {
            dir.push_back((dir[j] + 1) % 4);
        }
 
        dsize = dir.size();
        //새로 생긴 방향들에 대해서만 이동을 진행
        for (int j = (dsize / 2); j < dsize; j++) {
            x += dx[dir[j]];
            y += dy[dir[j]];
            check[y][x] = true;
        }
    }
 
}
 
 
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
 
    int n;
    cin >> n;
 
    while (n-- > 0) {
        cin >> x >> y >> d >> g;
        
        //새로운 드래곤 커브를 구해 줄 것이므로 방향을 저장해 놓을 벡터를 비워준다. 
        dir.clear();
        solve();
    }
 
 
    //드래곤 커브를 모두 구하고 한 번에 계산해준다
    cout << calc() << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 14503. 로봇 청소기  (0) 2019.08.04
[BOJ] 14502. 연구소  (0) 2019.08.04
[BOJ] 15686. 치킨 배달  (0) 2019.08.04
[BOJ] 16234. 인구 이동  (0) 2019.08.02
[BOJ] 17281. ⚾  (0) 2019.08.02

+ Recent posts