题目描述
曾经有一款流行的游戏,叫做 Infinity Loop,先来简单的介绍一下这个游戏:
游戏在一个 n×mn \times mn×m 的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中点有接口,所有水管的粗细都相同,所以如果两个相邻方格的公共边界的中点都有接头,那么可以看作这两个接头互相连接。水管有以下 151515 种形状:
游戏开始时,棋盘中水管可能存在漏水的地方。
形式化地:如果存在某个接头,没有和其它接头相连接,那么它就是一个漏水的地方。
玩家可以进行一种操作:选定一个含有非直线型水管的方格,将其中的水管绕方格中心顺时针或逆时针旋转 909090 度。
直线型水管是指左图里中间一行的两种水管。
现给出一个初始局面,请问最少进行多少次操作可以使棋盘上不存在漏水的地方。
输入
第一行两个正整数 n,mn,mn,m,代表网格的大小。
接下来 nnn 行每行 mmm 个数,每个数是 [0,15][0,15][0,15] 中的一个,你可以将其看作一个 444 位的二进制数,从低到高每一位分别代表初始局面中这个格子上、右、下、左方向上是否有 水管接头。
特别地,如果这个数是 000,则意味着这个位置没有水管。
比如 3(0011(2))3(0011_{(2)})3(0011(2)) 代表上和右有接头,也就是一个 L 型,而 12(1100(2))12(1100_{(2)})12(1100(2)) 代表下和左有接头,也就是将 L 型旋转 180180180 度。
输出
输出共一行,表示最少操作次数。如果无法达成目标,输出 −1-1−1.
样例输入
2 3
3 14 12
3 11 12
样例输出
2
提示
测试点编号 | n×mn\times mn×m 的范围 | 特殊约定 |
---|---|---|
1 | n×m≤16n\times m \le 16n×m≤16 | 无特殊要求 |
2 | ||
3 | n×m≤2000n \times m \le 2000n×m≤2000 | min(n,m)≤15\min(n,m) \le 15min(n,m)≤15 |
4 | ||
5 | ||
6 | ||
7 | ||
8 | ||
9 | 无特殊要求 | |
10 | ||
11 | ||
12 | ||
13 | ||
14 | ||
15 | ||
16 | ||
17 | ||
18 | ||
19 | ||
20 |
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxN = 1e4 + 5;
const int maxe = 4e6 + 5;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int n, m, s, t, TOT = 0;
struct Edge
{
int nxt, from, to, cap, cos;
}e[maxe];
int head[maxN], ecnt = -1;
In void addEdge(int x, int y, int w, int c, int flg) //black to white
{
if(y != t && flg) swap(x, y); //if x is white, swap x and y
e[++ecnt] = (Edge){head[x], x, y, w, c};
head[x] = ecnt;
e[++ecnt] = (Edge){head[y], y, x, 0, -c};
head[y] = ecnt;
}
In int num(int x, int y, int z)
{
return ((m * (x - 1) + y - 1) << 2) + z + 1;
}
#define U(x, y) num(x, y, 0)
#define R(x, y) num(x, y, 1)
#define D(x, y) num(x, y, 2)
#define L(x, y) num(x, y, 3)
In void build(int x, int y, int z, int flg)
{
int tp = 0;
for(int i = 0; i < 4; ++i) //to start/end
if((z >> i) & 1)
{
++TOT; ++tp;
if((x + y) & 1) addEdge(num(x, y, i), t, 1, 0, flg);
else addEdge(s, num(x, y, i), 1, 0, flg);
}
int turn = 0;
if(tp == 1)
{
if(z == 2) turn = 1;
if(z == 4) turn = 2;
if(z == 8) turn = 3;
int u = num(x, y, turn);
addEdge(u, num(x, y, (turn + 3) % 4), 1, 1, flg);
addEdge(u, num(x, y, (turn + 1) % 4), 1, 1, flg);
addEdge(u, num(x, y, (turn + 2) % 4), 1, 2, flg);
}
if(tp == 2)
{
if(z == 6) turn = 1;
if(z == 12) turn = 2;
if(z == 9) turn = 3;
if(z != 5 && z != 10)
{
int u = num(x, y, turn), v = num(x, y, (turn + 1) % 4);
addEdge(u, num(x, y, (turn + 2) % 4), 1, 1, flg);
addEdge(v, num(x, y, (turn + 3) % 4), 1, 1, flg);
}
}
if(tp == 3)
{
if(z == 7) turn = 1;
if(z == 14) turn = 2;
if(z == 13) turn = 3;
int v = num(x, y, (turn + 2) % 4);
addEdge(num(x, y, turn), v, 1, 2, flg);
addEdge(num(x, y, (turn + 1) % 4), v, 1, 1, flg);
addEdge(num(x, y, (turn + 3) % 4), v, 1, 1, flg);
}
}
bool in[maxN];
int dis[maxN], pre[maxN], flow[maxN];
In bool spfa()
{
Mem(dis, 0x3f); Mem(in, 0);
queue<int> q; q.push(s);
in[s] = 1; dis[s] = 0; flow[s] = INF;
while(!q.empty())
{
int now = q.front(); q.pop(); in[now] = 0;
for(int i = head[now], v; ~i; i = e[i].nxt)
{
v = e[i].to;
if(e[i].cap && dis[now] + e[i].cos < dis[v])
{
dis[v] = dis[now] + e[i].cos;
pre[v] = i;
flow[v] = min(flow[now], e[i].cap);
if(!in[v]) in[v] = 1, q.push(v);
}
}
}
return dis[t] ^ INF;
}
int maxFlow = 0, minCost = 0;
In void update()
{
int x = t;
while(x ^ s)
{
int i = pre[x];
e[i].cap -= flow[t];
e[i ^ 1].cap += flow[t];
x = e[i].from;
}
maxFlow += flow[t];
minCost += flow[t] * dis[t];
}
In void MCMF()
{
while(spfa()) update();
}
int main()
{
Mem(head, -1);
n = read(), m = read();
s = 0, t = n * m * 4 + 1;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
{
int x = read();
build(i, j, x, (i + j) & 1);
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
{
if(j < m) addEdge(R(i, j), L(i, j + 1), 1, 0, (i + j) & 1);
if(i < n) addEdge(D(i, j), U(i + 1, j), 1, 0, (i + j) & 1);
}
MCMF();
write(maxFlow == (TOT >> 1) ? minCost : -1), enter;
return 0;
}