본문 바로가기
Algorithm

[백준 17472] 다리만들기 2

by YEON-DU 2020. 2. 10.
반응형

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

 

17472번: 다리 만들기 2

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.

www.acmicpc.net

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>

using namespace std;

// 다리 만들기 2
// 다리는 ㅣ ㅡ 방향이 있고 해당 방향의 위 아래나 왼쪽 오른쪽에 연결할 나라가 있어야 한다.
// 다리의 길이는 1보다 커야한다.
// 교차하는 다리의 경우 각각의 다리의 크기를 포함한다.
// 0은 바다 1은 땅

// 다리를 만드는 방법.
// 각 섬에 인덱스를 붙여주고, 다리를 세워본다.
// 다리를 세운 뒤 모든 섬에 방문할 수 있으면 통과. 최단거리 구한 뒤 선택한다.

typedef pair<int, int> point;
const int INF = 987654321;

int N, M;
int island_count;
int map[10][10];
int index_map[10][10];
int dist[10][10];
bool visited[10][10];

int answer = INF;
int isUsed[49];

vector <point> maps;
vector <pair<point, int>> bridges; // bridges.first = (a, b), bridges.second = c, a섬에서 b까지 가는 거리 c
vector <point> islands[7]; // 섬의 좌표

point dir[4] = {{1,0},{-1,0},{0,1},{0,-1}};

void dfs(int r, int c, point d, int st, int ed, int distance)
{
    int tr = r + d.first;
    int tc = c + d.second;

    if(tr < 0 || tr >= N) return;
    if(tc < 0 || tc >= M) return;
    
    if(map[tr][tc] == 0) // 아직 바다라면
        dfs(tr, tc, d, st, ed, distance + 1); // 계속 다리 세우기
    else {
        if(index_map[tr][tc] == ed) { // 땅에 닿았는데 해당 지점이 목표로 하던 ed 섬이라면
            if(distance > 1) // 다리 길이는 1보다 커야한다
            {
                if(dist[st][ed] > distance) // 저장한 dist보다 작다면
                {
                    dist[st][ed] = distance;
                    dist[ed][st] = distance;
                }
                return;
            }
        }
        else
            return;
    }
    
}

void make_brige(int st, int ed) {
    for(int i = 0; i<islands[st].size(); i++)
    {
        int r = islands[st][i].first;
        int c = islands[st][i].second; // st섬의 좌표
        
        for(int j = 0; j<4; j++) // 4방향으로 st섬에서 ed섬으로 출발
        {
            int tr = r + dir[j].first;
            int tc = c + dir[j].second;
            
            if(tr < 0 || tr >= N) continue;
            if(tc < 0 || tc >= M) continue;
            
            if(map[tr][tc] == 0)
                dfs(r, c, dir[j], st, ed, 0); // 바다라면 다리를 세워서 st에서 ed까지 가는 최단 거리 갱신
        }
    }
}

void getMinDistance() // 다익스트라
{
    for(int i = 1; i < 7; i++)
        for(int j = 1; j < 7; j++)
            dist[i][j] = INF;
    
    for(int i = 1; i <= island_count; i++)
    {
        for(int j = i+1; j <= island_count; j++)
        {
            make_brige(i, j); // 다리 만들기
            if(dist[i][j] == INF) continue; // 여전히 i섬에서 j섬까지 가는 거리가 infinity라면
            bridges.push_back({{i,j}, dist[i][j]});
        }
    }
}

void bfs(int r, int c, int num)
{
    queue <point> que;
    que.push({r, c});
    visited[r][c] = true;
    index_map[r][c] = num;
    islands[num].push_back({r, c});
    
    while(!que.empty())
    {
        int rr = que.front().first;
        int cc = que.front().second;
        que.pop();
        
        for(int i = 0; i < 4; i++)
        {
            int tr = rr + dir[i].first;
            int tc = cc + dir[i].second;
            
            if(tr < 0 || tr >= N) continue;
            if(tc < 0 || tc >= M) continue;
            
            if(!visited[tr][tc] && map[tr][tc] == 1)
            {
                visited[tr][tc] = true;
                index_map[tr][tc] = num;
                que.push({tr, tc});
                islands[num].push_back({tr, tc});
            }
        }
    }
}

int indexing() {
    int island = 1;
    
    for(int i = 0; i < maps.size(); i++)
    {
        int r = maps[i].first;
        int c = maps[i].second;
        
        if(!visited[r][c])
        {
            bfs(r, c, island); // 섬에 인덱스 붙여주기
            island++;
        }
    }
    return island; // 섬의 개수 반환
}

bool isConnected() {
    
    bool canGoIsland[7][7];
    bool checkConnect[7];
    memset(canGoIsland, false, sizeof(canGoIsland));
    memset(checkConnect, false, sizeof(checkConnect));
    
    for(int i = 0; i<bridges.size(); i++)
    {
        if(isUsed[i])
        {
            int st = bridges[i].first.first;
            int ed = bridges[i].first.second;
            
            canGoIsland[st][ed] = true;
            canGoIsland[ed][st] = true; // st에서 ed로 이동하는 다리를 선택했을 때
        }
    }
    
    queue <int> que;
    que.push(1);
    int connected_count = 1;
    checkConnect[1] = true; // 첫 번째 섬에서 시작
    
    while(!que.empty())
    {
        int front = que.front();
        que.pop();
        
        for(int i = 1; i<=island_count; i++)
        {
            if(front == i) continue; // 현재 섬이면

            if(canGoIsland[front][i] && !checkConnect[i]) // 현재 섬에서 i섬에 갈 수 있다면
            {
                checkConnect[i] = true;
                que.push(i);
                connected_count ++;
                
                if(connected_count == island_count)
                    return true;
            }
        }
    }
    
    return false;
}

void solve(int idx, int count, int sum)
{
    if(count >= 1)
        if(isConnected()) // 만약 한 지점에서 모든 곳에 갈 수 있다면
            answer = (answer > sum) ? sum : answer;
    
    for(int i = idx; i<bridges.size(); i++)
    {
        if(!isUsed[i])
        {
            isUsed[i] = true;
            solve(i, count + 1, sum + bridges[i].second); // 다리 개수를 늘려주고, 현재 합 + 다음 다리의 최소 길이
            isUsed[i] = false;
        }
    }
}

int main(){
    
    cin >> N >> M;
    
    for(int i =0; i<N; i++)
        for(int j =0; j<M; j++)
        {
            cin >> map[i][j];
            
            if(map[i][j] == 1)
                maps.push_back({i,j}); // 섬인 경우 좌표 저장
        }
    
    island_count = indexing() - 1;
    getMinDistance();
    solve(0, 0, 0);
    
    if(answer == INF)
        answer = -1;
    
    cout << answer;
    
    return 0;
}

 

처음에 st 섬에서 ed 섬에 가는 걸 누적되게 세어가면서 connected_island를 세어줬었는데,

생각해보니 한 섬에 여러 섬이 연결되어있는 경우를 빠뜨려서 처음에 오답이 떴었다.

예시 문제는 또 맞았던 터라 질문 중에서 반례를 찾았던 게 아래 예시.

 

10 10
1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 0 1 0 1 0 1 0 1 0​

한 섬이 있고 모든 섬이 그 섬에 연결되어 있다.

correct answer는 40이 나오는 게 정답이고, -1이 나와서 다시 생각해봐야 했다.

반응형

'Algorithm' 카테고리의 다른 글

[백준 4358] 생태학  (0) 2020.06.15
X-code 특정 파일 배제하고 빌드하기  (2) 2020.02.10
[백준 17142] 연구소 3  (0) 2020.02.10
[백준 17281] ⚾  (0) 2020.01.27
[백준 1780] 종이의 개수  (0) 2020.01.26

댓글