染方块-color
Description
现在有一个
n
×
n
n\times n
n×n 的方格,每一个格子是红块(X
),绿块 (O
),或者空白块 (.
) ,而你可以把空白块染成红色。
在你对你想染色的空白块进行染色之后,方格会有这样的变化:如果一个绿色块上下左右都是红块,这个绿块就会变成空白块。
给定方格的初始状态,你现在可以对其进行符合条件的染色,求空格最大能达到多少个。
为了方便处理,我们保证没有任意两个绿块边相邻,任意一个绿块上下左右必定有一个空白块,同时假定棋盘边界外的部分全都是红块。
Input
n n n 行字符串,每行字符串有 n n n个字符,从左上到右下表示这个棋盘的状态。
Output
一行一个整数 a n s ans ans , 表示经过染色后最多能有多少个空格。
Sample Input 1
.XOX.
.O.OX
X.O.O
OX.OX
.OX..
Sample Output 1
12
Hint
样例解释:
.X.X.
.OX.X
X.OX.
.X.OX
X.X..
数据范围
n ≤ 50 n \leq 50 n≤50
题意:
要求将若干空白变成红块,,四个红块围起来可以将中间的绿块变成空白,要使得最后的空白块最多。
思路:
看看题目,极其像网络流,但它确实是一个网络流 然而我们流了快两个小时都没流出来。。。。。
先说说当时的思路,因为绿块周围全都都是红块时,绿块会变空白,这样就将绿块周围的白块与绿块建边(流量为1,费用为-1),每个绿块都进行相同的操作,再建立超级源点和汇点,跑最小费用流。但是我们当时无论怎么建图都不能将处理同一个空白块对多个绿块造成影响的情况其实并不是这么做的,也有可能是我不会建图。。。。。
真正的解法:跑一边最大流就可以,可以反过来考虑,绿块四周任意一个空白块不放,那么绿块就不能变白,那么建边就表示,通过不将该白块变红,使得绿块也不能变白。
先将图中所有的白块和绿块个数统计出来,记为 a n s ans ans,,然后建图,跑最大流,用 a n s ans ans 减去最大流就是答案。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<cmath>
#include<map>
#include<cstring>
#include<algorithm>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
const int MAX_V = 2510;
struct MAXFLOW{
struct edge {
int to, cap, rev;
edge() {}
edge(int _t, int _c, int _r) : to(_t), cap(_c), rev(_r) {}
};
vector<edge> G[MAX_V];
// bool vis[MAX_V];
int level[MAX_V], iter[MAX_V], V;
void add_edge (int from, int to, int cap) {
G[from].push_back(edge(to, cap, G[to].size()));
G[to].push_back(edge(from, 0, G[from].size() - 1));
}
void bfs(int s, int t) { //bfs分层,若level[t]为-1,就说明不存在增广路了
// memset(level, -1, sizeof(level));
for (int i = 0; i <= V; i++) level[i] = -1;
queue<int> que;
level[s] = 0;
que.push(s);
while (!que.empty()) {
int v = que.front(); que.pop();
for (int i = 0; i < (int)G[v].size(); i++) {
edge &e = G[v][i];
if (e.cap > 0 && level[e.to] < 0) {
level[e.to] = level[v] + 1;
que.push(e.to);
}
}
}
}
ll dfs(int v, int t, int f) { //寻找增广路,t为汇点,f为流量
if (v == t) return f;
ll ret = 0, d; //当前弧优化,确保走过的边不再增广
for (int &i = iter[v]; i < (int)G[v].size(); i++) {
edge &e = G[v][i];
if (e.cap > 0 && level[v] + 1 == level[e.to]) { //层数大一,确保增广最短
d = dfs(e.to, t, min(f, e.cap));
e.cap -= d;
G[e.to][e.rev].cap += d;
f -= d; ret += d; //继续在残余网络中增广
if (!f) break;
}
}
return ret;
}
ll Dinic(int s, int t) {
ll flow = 0;
while (true) {
bfs(s, t);
if (level[t] < 0) return flow; //残余网络中不存在s到t的增广路径了
// memset(iter, 0, sizeof(iter));
for (int i = 0; i <= V; i++) iter[i] = 0;
flow += dfs(s, t, INF);
}
}
void init(int _n) {
V = _n;
for (int i = 0; i <= V; i++) G[i].clear();
}
}mf;
int n;
char s[55][55];
int pos[55][55];
bool is_in(int x, int y) {
if (x < 0 || x >= n || y < 0 || y >= n) return false;
if (s[x][y] == '.') return true;
else return false;
}
int main() {
scanf("%s", s[0]);
n = strlen(s[0]);
for (int i = 1; i < n; i++) {
scanf("%s", s[i]);
}
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (s[i][j] == 'O' || s[i][j] == '.')
ans++;
pos[i][j] = i * n + j;
}
}
int st = n * n, ed = n * n + 1;
mf.init(ed);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (s[i][j] == 'O') {
if (is_in(i + 1, j))
mf.add_edge(pos[i + 1][j], pos[i][j], 1);
if (is_in(i, j + 1))
mf.add_edge(pos[i][j + 1], pos[i][j], 1);
if (is_in(i - 1, j))
mf.add_edge(pos[i - 1][j], pos[i][j], 1);
if (is_in(i, j - 1))
mf.add_edge(pos[i][j - 1], pos[i][j], 1);
mf.add_edge(pos[i][j], ed, 1);
}
else if (s[i][j] == '.') {
mf.add_edge(st, pos[i][j], 1);
}
}
}
ans = ans - mf.Dinic(st, ed);
printf("%d\n", ans);
return 0;
}