일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 후기
- bixby studio
- INNER JOIN
- maximum flow
- Baekjoon
- 이분탐색
- 메모이제이션
- SQL
- Network Flow
- backjoon
- 코딩테스트
- ICPC
- 백준
- 알고리즘
- SWTest
- DP
- 빅스비
- 삼성
- 최대유량
- 최대 유량
- JOIN
- 완전탐색
- SDS 알고특강
- SWEA
- 네트워크 플로우
- 프로그래머스
- 빅스비 스튜디오
- BOJ
- 분할정복
- 세그먼트트리
- Today
- Total
답은 알고리즘 뿐이야!
[BOJ 11495] 격자 0 만들기 본문
문제 출처 : https://www.acmicpc.net/problem/11495
11495번: 격자 0 만들기
첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫째 줄에는 n과 m (2 ≤ n, m ≤ 50)이 주어지며, n은 격자의 행 개수, m은 격자의 열 개수를 나타낸다. 그 다음 n개의 줄에 각각 ��
www.acmicpc.net
풀이 :
네트워크 플로우 최대 유량 문제입니다.
아래의 그림처럼 격자를 체스판처럼 나눠서
Source -> 빨간색 영역 -> 사방의 하얀색 영역 -> Sink
이런식으로 모델링 해 주시면 가로 세로 블럭에 대한 모델링은 완성입니다.

그 다음 최소 연산 횟수를 구해야 하는데
위의 모델링으로 구할 수 있는 연산 횟수는
" 둘다 양수인 격자에 대한 최소 연산 횟수 " 입니다.
따라서 양수와 0이 묶인 격자에 대한 처리는 하지 않은 상태입니다.
이에 대한 추가 연산을 해주어야하는데
Maximum Flow 를 F라 두었을때,
전체 격자판의 합 sum 에서 2F를 빼면 양수와 0이 묶인 격자에 대한 연산횟수만 남게됩니다.
예를 들면
1 1 1
1 0 1
인 격자판에서의 최대 유량은 2가 될것입니다.
sum - 2F = 1
따라서 0과 정수가 묶인 격자에 대한 연산횟수는 1이 됩니다.
0과 정수가 묶인 격자에 대한 연산횟수를 X라 두었을때, 위의 식을 고쳐 쓰면
sum - 2F = X
sum - F = X + F
가 됩니다.
따라서 격자를 0으로 만들기 위한 최소 연산 횟수는 전체 격자판의 합(sum) - MaximumFlow(F) 가 됩니다.
#include<iostream> | |
#include<algorithm> | |
#include<vector> | |
#include<queue> | |
#define ms 2502 | |
using namespace std; | |
struct MF { | |
struct edge { | |
int to, c, f; | |
edge *d; | |
edge(int to1 = -1, int c1 = 0) :to(to1), c(c1), f(0), d(nullptr) {} | |
int spare() { return c - f; } | |
void addFlow(int f1) { | |
f += f1; | |
d->f -= f1; | |
} | |
}; | |
int S = 0, E = ms - 1, mf = 0, t, n, m, map[50][50]; | |
vector<edge*>adj[ms]; | |
void init() { | |
for (int i = 0; i < ms; i++)adj[i].clear(); | |
mf = 0; | |
} | |
void addEdge(int u, int v, int c) { | |
edge *e1 = new edge(v, c), *e2 = new edge(u, 0); | |
e1->d = e2; | |
e2->d = e1; | |
adj[u].push_back(e1); | |
adj[v].push_back(e2); | |
} | |
int solve() { | |
while (1) { | |
int pre[ms]; | |
for (int i = 0; i < ms; i++)pre[i] = -1; | |
edge* path[ms] = { 0 }; | |
queue<int> q; | |
q.push(S); | |
while (!q.empty()) { | |
int now = q.front(); | |
q.pop(); | |
for (edge *e : adj[now]) { | |
int next = e->to; | |
if (pre[next] == -1 && e->spare() > 0) { | |
pre[next] = now; | |
path[next] = e; | |
q.push(next); | |
if (next == E)break; | |
} | |
} | |
} | |
if (pre[E] == -1)break; | |
int flow = 1e9; | |
for (int i = E; i != S; i = pre[i])flow = min(flow, path[i]->spare()); | |
for (int i = E; i != S; i = pre[i])path[i]->addFlow(flow); | |
mf += flow; | |
} | |
return mf; | |
} | |
void inp() { | |
cin >> t; | |
while (t--) { | |
init(); | |
cin >> n >> m; | |
int sum = 0; | |
for (int i = 0; i < n; i++)for (int j = 0; j < m; j++)cin >> map[i][j], sum += map[i][j]; | |
for (int i = 0; i < n; i++)for (int j = 0; j < m; j++) { | |
int now = i * m + j + 1; | |
if ((i + j) % 2) { | |
addEdge(now, E, map[i][j]); | |
continue; | |
} | |
addEdge(S, now, map[i][j]); | |
if (i - 1 >= 0)addEdge(now, now - m, map[i - 1][j]); | |
if (i + 1 < n)addEdge(now, now + m, map[i + 1][j]); | |
if (j - 1 >= 0)addEdge(now, now - 1, map[i][j - 1]); | |
if (j + 1 < m)addEdge(now, now + 1, map[i][j + 1]); | |
} | |
cout << sum - solve() << '\n'; | |
} | |
} | |
}; | |
int main() { | |
ios::sync_with_stdio(false), cin.tie(0); | |
MF m; | |
m.inp(); | |
} |
'알고리즘 > 백준문제풀이' 카테고리의 다른 글
[BOJ 1525] 퍼즐 (0) | 2021.01.19 |
---|---|
[BOJ 16978] 수열과 쿼리 22 (0) | 2020.09.04 |
[BOJ 2365] 숫자판 만들기 (0) | 2020.09.03 |
[BOJ 1760] N-Rook (0) | 2020.09.03 |
[BOJ 2414] 게시판 구멍 막기 (0) | 2020.09.03 |