본문 바로가기
알고리즘/알고리즘 오답노트

[BFS+브루트포스] 16988 : Baaaaaaaaaduk2 (Easy)

by shinyou1024 2019. 3. 11.

스터디 9주차


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


/*

 백준 16988 : Baaaaaaaaaduk2 (Easy)

 분류 : 브루트포스 + BFS

 내 바둑돌을 검은돌, 상대 바둑돌을 흰돌로 생각하고 풀었음

 

 풀이 : 빈 칸마다 검을돌 두 개를 놓는다고 가정하고(브루트포스), 그 상황에서 죽는 흰돌의 개수를 세어준다(BFS)

    1. 빈 칸마다 검은돌 두 개를 놓는다고 가정(브루트 포스)

        => 처음에 입력받을 때 빈 칸을 미리 판별해서 for문 돌릴 때 빈칸에 바둑돌 놓는 것만 생각했음

        => 각 경우의 수마다 board에 놓았던 바둑돌을 제거하고, visited를 초기화해줘야함 (중요)

    2. BFS : 그렇게 놓인 상황에서 다시 이중for문을 돌려 흰돌을 발견하면 BFS로 죽는 흰돌개수를 세어준다

        1) 전체 브루트포스과정에서 쓰이는 visited말고 한 BFS과정에서 쓸 checked배열을 만들어줌

        2) 상하좌우를 검사한다

            - 빈칸일경우 : 아예 둘러싸이는 조건이 성립하지 않으므로 함수가 종료

            - 모서리, 검은돌, 이미검사한흰돌일 경우 : cnt++

            - 아직 검사 안 한 흰돌일 경우 : 큐에 넣고 BFS를 돌려준다! (재귀는 아니고 while문으로)

        3) 큐에 더 이상 들어갈 흰돌이 없어서 while문이 끝나면

            cnt가 몇인지 확인해서 4이면 둘러싸인 걸로 판별해서 candidate들을 싹 killed로 넣어준다

    3. 각 BFS를 통해 확보한 '죽는 흰돌개수'를 killed에 더해줌

    4. 이중 포문이 한 번 끝날 때마다 새로 계산된 killed와 ans를 비교해 더 큰 값을 ans로 갱신해준다

 

 오답노트

    1. DFS로 풀려고 했었는데 어디선가 꼬였음

    2. 바둑돌 두 개를 놓는 경우의 수마다 판별해야 하기 때문에 visited[k][h]를 초기화해줘야함!!!!

    3. 죽는 흰 돌 개수를 반환할 때 killed += candidate이렇게 하면 중복돼서 세어짐

    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
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
 
#include <iostream>
#include <queue>
#include <vector>
#include <string.h>
using namespace std;
int r, c;
int board[21][21];
vector<pair<int,int> > empty;
int dy[4]={-1100};
int dx[4]={00-11};
bool visited[21][21]={0}; // 흰 돌을 방문했었는지 여부(이미 확인한애는 안 해도 됨)
int size//
int ans;
 
 
// 죽는 흰 돌의 개수를 알려주는 bfs
int bfs(int y, int x) {
    int killed=0;  //죽인 상대 돌의 총 개수
    // 이미 방문한 점이면 리턴
    if(visited[y][x])
        return 0;
    
    queue<pair<intint> > q;
    q.push(make_pair(y,x));
    
    visited[y][x]=true;
    
    bool checked[21][21]={false};
    // 죽일 후보 수
    int candidate=0;
    while(!q.empty()) {
        y = q.front().first;
        x = q.front().second;
        
        //cout<<y<<","<<x<<"판단"<<endl;
        
        checked[y][x]=true;
        candidate++;
        //cout<<"candidate"<<candidate<<endl;
        q.pop();
        int cnt=0// 큐에서 꺼낸 돌의 상하좌우를 검색했을 때, 1이거나 이미 방문한 2일경우의 수를 셈(4면막힌것)
        for(int d=0; d<4; d++) {
            int ny = y+dy[d];
            int nx = x+dx[d];
            
            if(0<=ny&&ny<r&&0<=nx&&nx<c) {
                // 인접한 빈칸이 있으면 뚫린 것 => 함수 반환
                if(board[ny][nx]==0) {
                    return 0;
                }
                if(board[ny][nx]==2&&checked[ny][nx]==false) {
                    checked[ny][nx]=true;
                    visited[ny][nx]=true;
                    q.push(make_pair(ny,nx));
                    // 일단 죽일 후보에 추가해줌
                    continue;
                }
                // 1돌 또는 이미 방문된 2돌일 경우
                if(board[ny][nx]==1||checked[ny][nx]==true)
                    cnt++;
            }
            // board의 모서리일 경우
            else {
                cnt++;
            }
            // 인접한 최후의 2돌까지 왔을때 막혔다고 판별난 경우
            
        }
        
        if(cnt==4)
            killed = candidate; // 이걸 killed += candidate으로 잘못 써놔서 고생...
    }
    //cout<<"죽인것:"<<killed<<endl;
    return killed;
    
}
int main() {
    cin>>r>>c;
    for(int i=0; i<r; i++) {
        for(int j=0; j<c; j++) {
            int input;
            cin>>input;
            board[i][j]=input;
            if(input==0) {
                empty.push_back(make_pair(i,j));
            }
        }
    }
    
    
    size = empty.size();
    
    // 1번 돌 두 개씩 놓기
    for(int i=0; i<size; i++) {
        int x1 = empty[i].second;
        int y1 = empty[i].first;
        board[y1][x1]=1;
        for(int j=i+1; j<size; j++) {
            int mykilled=0;
            int x2 = empty[j].second;
            int y2 = empty[j].first;
            //cout<<y1<<","<<x1<<"이랑";
            //cout<<y2<<","<<x2<<endl;
            board[y2][x2]=1;
            
            for(int k=0; k<r; k++) {
                for(int h=0; h<c; h++) {
                    //2돌이면 bfs돌리기
                    if(board[k][h]==2)
                        mykilled += bfs(k,h);
                }
            }
            // 더 큰 값을 ans에 저장
            ans=max(ans, mykilled);
            // 다음 수를 계산하기 위해 초기화
            board[y2][x2]=0;
            // 이거안해줘서망할뻔 : 다음 수를 계산하기 위해 방문기록 지우기
            memset(visited, 0sizeof(visited));
        }
        // 다음 수를 계산하기 위해 초기화
        board[y1][x1]=0;
    }
    cout<<ans<<endl;
    
    return 0;
}
 
cs


'알고리즘 > 알고리즘 오답노트' 카테고리의 다른 글

6679 싱기한 네자리 숫자  (0) 2019.03.13
2688 줄어들지 않아  (0) 2019.03.13
1535 안녕  (0) 2019.03.05
2644 촌수계산  (0) 2019.03.04
1233 주사위  (0) 2019.03.04

댓글