반응형

문제 출처 :


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



알고리즘 분석 :


문제 해결에 필요한 사항

1. 이분 매칭 :: http://www.crocus.co.kr/499

2. 최대 유량 :: http://www.crocus.co.kr/741


이 문제는 이분 매칭 혹은 최대 유량(유량이 1이라 생각하여) 풀 수 있다.


이번에는 이분 매칭으로 풀 것인데


이 문제는 이분 매칭을 위해 이분 그래프로 나타내는 것이 매우 중요하다.


이 문제를 풀고나면 혹은 이 문제를 풀기 전에 

[2414번] 게시판 구멍 막기 :: http://www.crocus.co.kr/750 문제도 풀어보길 추천한다.



문제에서 이분 그래프로 나타내는 방식은 다음과 같다.


3 4
2 0 0 0
2 2 2 1
0 1 0 2


예제 입력을 이용하여 이분 그래프를 만들어보자.


이분 그래프를 만들기 위해 두가지로 명확하게 나뉘는 것은 행, 열이다.


따라서 행렬을 이용한다고 생각하여 이분 그래프를 만들 것이다.


룩은 자신이 위치한 행,열을 공격할 수 있다. 

따라서 x,y좌표에 룩이 위치한다면 처음~x~끝,y 그리고 x, 처음~y~끝에는 다른 룩이 올 수 없다. 


따라서 그룹화를 다음과 같이 한다.


행 기준으로 넘버링시 ::

1.  0,1이 연속될 경우 같은 그룹으로 넘버링

2.  중간에 2가 있을 시 다른 그룹으로 넘버링(벽이 있으면 룩이 공격을 할 수 없다. 같은 행에 있어도)




열 기준으로 넘버링시 ::

1.  0,1이 연속될 경우 같은 그룹으로 넘버링

2.  중간에 2가 있을 시 다른 그룹으로 넘버링(벽이 있으면 룩이 공격을 할 수 없다. 같은 열에 있어도)




이를 토대로 

1-2, 1-4, 1-6, 3-1, 3-5를 연결해준다.

(여기서 연결할 때는 1인 곳에는 놓을 수 없으니, 1인 부분은 이분 매칭에 이용되지 못하도록 한다.)



여기서 매칭을 시켜보면 무슨 일이 일어날까?



열의 1번 행의 3번에 룩이 하나 위치, 열의 2번 행의 1번에 룩이 하나 위치하게 된다.


예제 입력에서도 보면 열의 1번 행의 3번에 룩이 하나 위치, 열의 2번 행의 1번에 룩이 하나 위치하게되면 

더이상 위치할 곳이 없게된다.


아래 코드에서 adj가 5002 5002인 이유는


넘버링 시 최악의 경우 5000번까지 생성 될 수 있다.


0 2 0

2 0 2

0 2 0 이런식일때





소스 코드 : 


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
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <vector>
#include <algorithm>
 
#define MAX_N 102
using namespace std;
 
int map[MAX_N][MAX_N];
int col[MAX_N][MAX_N];
int row[MAX_N][MAX_N];
 
bool adj[5002][5002];
vector<int> aMatch;
vector<int> bMatch;
int visit[5002];
int visitCnt;
 
int n, m;
 
bool dfs(int a)
{
    if (visit[a] == visitCnt)
        return false;
 
    visit[a] = visitCnt;
 
    for (int b = 1; b <= m; b++)
    {
        if(adj[a][b])
        {
            if (bMatch[b] == -|| dfs(bMatch[b]))
            {
                aMatch[a] = b;
                bMatch[b] = a;
 
                return true;
            }
        }
    }
 
    return false;
}
 
int bipartiteMatch()
{
    aMatch = vector<int>(n + 1-1);
    bMatch = vector<int>(m + 1-1);
 
    int ans = 0;
    
    for (int a = 1; a <= n; a++)
    {
        visitCnt++;
        ans += dfs(a);
    }
 
    return ans;
}
 
int main()
{
    scanf("%d %d"&n, &m);
 
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            scanf("%d"&map[i][j]);
    
    int cnt = 1;
 
    // 행 기준으로 넘버링
    bool chk;
    for (int i = 1; i <= n; i++)
    {
        chk = false;
 
        for (int j = 1; j <= m; j++)
        {
            if (map[i][j] == || map[i][j] == 1)
            {
                col[i][j] = cnt;
                chk = true;
            }
            
            else
                if (chk)
                {
                    cnt++;
                    chk = false;
                }
        }
 
        if (chk)
            cnt++;
            
    }
 
    // 열 기준으로 넘버링
    cnt = 1;
    for (int j = 1; j <= m; j++)
    {
        chk = false;
 
        for (int i = 1; i <= n; i++)
        {
            if (map[i][j] == || map[i][j] == 1)
            {
                row[i][j] = cnt;
                chk = true;
            }
            
            else
                if (chk)
                {
                    cnt++;
                    chk = false;
                }
        }
 
        if (chk)
            cnt++;
    }
 
    int maxR = 0, maxC = 0;
 
    // 2는 당연히 안되고, 
    // 1인 곳도 이분 매칭에 연결되지 않도록 해준다.(룩을 놓을 수 없다.)
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (map[i][j] == 0)
            {
                // adj 범위가 여기 때문에 adj[5002][5002]로 설정
                adj[row[i][j]][col[i][j]] = 1;
 
                maxR = max(maxR, row[i][j]);
                maxC = max(maxC, col[i][j]);
            }
 
    // 이제는 n,m으로 보는 것이 아닌 넘버링 단위로 봐야한다.
    n = maxR;
    m = maxC;
 
    int get = bipartiteMatch();
 
    printf("%d", get);
 
    return 0;
}
 
//                                                       This source code Copyright belongs to Crocus
//                                                        If you want to see more? click here >>
Crocus


반응형

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

[1252번] 이진수 덧셈  (0) 2017.04.14
[1574번] 룩 어택  (0) 2017.04.14
[1671번] 상어의 저녁식사  (0) 2017.04.14
[9576번] 책 나눠주기  (0) 2017.04.14
[2573번] 빙산  (0) 2017.04.14