baekjoon
[백준] 11495 격자 0 만들기
윤만석
2023. 4. 10. 10:49
문제
음수가 아닌 정수들의 격자가 주어진다. 당신은 이 격자에 다음 연산을 행할 수 있다.
- 1. 격자에서 가로 또는 세로로 인접한 정수 2개를 고른다.
- 2. 각 정수가 양수일 때 1 감소시킨다.
다음 그림은 총 4개의 연속한 연산을 2*2 격자에 가해서 모든 정수를 0으로 만든 과정을 보여준다.

위 예제에서는 모든 정수를 0으로 만들기 위해 4번의 연산을 행했다. 이보다 적은 횟수의 연산으로는 모든 정수를 0으로 만들 수 없다는 것을 쉽게 알 수 있다.
격자가 주어졌을 때 모든 정수를 0으로 만들기 위해 필요한 최소 연산의 횟수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫째 줄에는 n과 m (2 ≤ n, m ≤ 50)이 주어지며, n은 격자의 행 개수, m은 격자의 열 개수를 나타낸다. 그 다음 n개의 줄에 각각 격자의 해당 행에 있는 m개의 정수가 열 순서대로 주어진다. 각 정수는 0 이상 1,000 이하이다.
출력
각 테스트 케이스마다 필요한 연산의 최소 횟수를 한 줄에 출력한다.
격자에서 가로,세로 한 쌍씩 묶어서 연산을 수행합니다.
이때 한 정점에 대해서는 그 정점의 숫자만큼 연산이 가능하고,
바로 옆에있는 정점과 같이 연산이 가능합니다.
따라서 격자무늬로 모델링이 가능합니다.
격자무늬로 모델링을하고, 소스또는 싱크에서 그 숫자만큼 capacity를 줍니다.
그리고 간선 간 함께 연산을 수행가능한 정점끼리 간선을 부여합니다.
이상태로 최대유량을 수행하고, 싱크또는 소스에서 capacity-flow(남는 간선)만큼만 더해주면 답입니다.
에드먼즈 카프 알고리즘으로 수행한 결과입니다.
#include<bits/stdc++.h>
#define FAST ios_base::sync_with_stdio(false),cin.tie(NULL);
#define mset(v) memset(v,0,sizeof(v));
#define rep(i,a) for(int i=0;i<a;++i)
#define REP(i,a) for(int i=1;i<=a;++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pi;
typedef tuple<int, int, int>ti;
typedef vector<int> vi;
typedef vector<vector<int>> vvi;
int dy[] = { -1,0,1,0 }, dx[] = { 0,1,0,-1 }, INF = 987654321, S = 3001, E = 3002, A, B = 1500;
vi adj[3003];
using namespace std;
int TC, N, M, x, idx[53][53], ans, c[3003][3003], f[3003][3003];
int main() {
FAST;
cin >> TC;
while (TC--) {
ans = 0;
mset(idx);
mset(c);
mset(f);
for (int i = 1; i <= 3002; i++) {
adj[i].clear();
}
A = 0, B = 1500;
cin >> N >> M;
REP(i, N)REP(j, M) {
cin >> x;
if ((i + j) % 2 == 0) {
idx[i][j] = ++A;
c[S][A] = x;
adj[S].push_back(A);
adj[A].push_back(S);
}
else {
idx[i][j] = ++B;
c[B][E] = x;
adj[B].push_back(E);
adj[E].push_back(B);
}
}
REP(i,N)REP(j,M){
rep(k, 4) {
int ny = i + dy[k];
int nx = j + dx[k];
if (ny >= 1 && ny <= N && nx >= 1 && nx <= M && (i+j)%2 == 0) {
c[idx[i][j]][idx[ny][nx]] = INF;
adj[idx[i][j]].push_back(idx[ny][nx]);
adj[idx[ny][nx]].push_back(idx[i][j]);
}
}
}
while (1) {
queue<int>q;
q.push(S);
int p[3003];
fill(p, p + 3003, -1);
while (!q.empty()) {
int cur = q.front();
q.pop();
for (int nxt : adj[cur]) {
if (p[nxt]==-1 && c[cur][nxt] - f[cur][nxt] > 0) {
p[nxt] = cur;
q.push(nxt);
}
}
}
if (p[E] == -1)break;
int flow = INF;
for (int i = E; i != S; i = p[i]) {
flow = min(flow, c[p[i]][i] - f[p[i]][i]);
}
for (int i = E; i != S; i = p[i]) {
f[p[i]][i] += flow;
f[i][p[i]] -= flow;
}
ans += flow;
}
for (int i = 1; i <= A; i++) {
ans += (c[S][i] - f[S][i]);
}
for (int i = 1501; i <= B; i++) {
ans += (c[i][E] - f[i][E]);
}
cout << ans << "\n";
}
}