Description
曾经有一款流行的游戏,叫做InfinityLoop,先来简单的介绍一下这个游戏:
游戏在一个n×m的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中点有接口,所有水管的粗细都相同,所以如果两个相邻方格的公共边界的中点都有接头,那么可以看作这两个接头互相连接。水管有以下15种形状:
游戏开始时,棋盘中水管可能存在漏水的地方。
形式化地:如果存在某个接头,没有和其它接头相连接,那么它就是一个漏水的地方。
玩家可以进行一种操作:选定一个含有非直线型水管的方格,将其中的水管绕方格中心顺时针或逆时针旋转90度。
直线型水管是指左图里中间一行的两种水管。
现给出一个初始局面,请问最少进行多少次操作可以使棋盘上不存在漏水的地方。
Input
第一行两个正整数n,m代表网格的大小。
接下来n行每行m数,每个数是[0,15]中的一个
你可以将其看作一个4位的二进制数,从低到高每一位分别代表初始局面中这个格子上、右、下、左方向上是否有水管接头。
特别地,如果这个数是000,则意味着这个位置没有水管。
比如3(0011(2))代表上和右有接头,也就是一个L型,而12(1100(2))代表下和左有接头,也就是将L型旋转180度。
n×m≤2000
Output
输出共一行,表示最少操作次数。如果无法达成目标,输出-1
Sample Input
2 3
3 14 12
3 11 12
Sample Output
2
HINT
样例1棋盘如下
旋转方法很显然,先将左上角虚线方格内的水管顺时针转90度
然后右下角虚线方格内的水管逆时针旋转90度,这样就使得水管封闭了
样例 2 为题目描述中的第一张图片,无法达成目标。
样例 3 为题目描述中的第二张图片,将除了中心方格以外的每个方格内的水管都转 180 度即可。
看题解之前一脸懵逼……看完题解后大骂自己是zz……
首先我们黑白染色,分成黑白两部分,S向白点连边,黑点向T连边
然后考虑触点的方向有4个,那么我们显然把每个点拆成5个点,上下左右各一个,外加自己这个中心点
然后我们就可以考虑连边,然后旋转有费用,我们就建图跑费用流,那么,怎么连边?或者说,如何控制边的流量和费用?
分类讨论?讨论旋转后每个触点的出边?恭喜你,你可能会讨论到死去活来,甚至最后会TLE
其实我们已经拆完点了,那么我们只需要在内部讨论费用问题即可,根本不需要考虑旋转后触点的出边
第一种:单触点
这个好办,它原本指向哪个方向,我们让中心点连向该方向的小点,流量为1,费用为0;至于旋转,我们只需要让小点连向其他小点即可,流量为\(\infty\),费用分别为1,2,1(蛤?你问我哪个是1,哪个是2?这种事自己画个图判断去吧,反正不难)
第二种:双触点
直线型直接pass吧(不知道为什么赶快看题去,这东西我整半天没整明白咋连费用,最后发现题目不让旋转)
拐角型的话,我们假定触点在上右两侧,那么我们中心点就连出两条流量为1,费用为0的边出来;然后考虑旋转,就让上侧小点向下侧小点连一条流量为\(\infty\),费用为1的边,右向左也如此。然后你稍作模拟,发现不管拐角型转成啥样,费用都是对的
第三种:三触点
其实和单触点类似,不过在连出三条费用为0的边后,直接连接的三个小点分别向空出来的小点连一条流量为\(\infty\),费用分别为1,2,1的边(说了和单触点类似,所以自己画图稍微判下吧)
第四种:全触点
那就全连边呗,都是费用为0的
然后图建完了,直接上最小费用最大流即可
然后这是EK费用流和Dinic费用流的区别……不说了,以后都用Dinic了……
/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
#define lowbit(x) ((x)&-(x))
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
int x=0,f=1; char ch=gc();
for (;ch<'0'||ch>'9';ch=gc()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
return x*f;
}
inline int read(){
int x=0,f=1; char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
return x*f;
}
inline void print(int x){
if (x<0) putchar('-'),x=-x;
if (x>9) print(x/10);
putchar(x%10+'0');
}
const int N=1e4+2,M=4e4;
const int dx[4]={-1,0,1,0};
const int dy[4]={0,1,0,-1};
int pre[M+10],now[N+10],child[M+10],val[M+10],cost[M+10];
int dis[N+10],deep[N+10];
int n,m,S,T,All,tot=1;
void join(int x,int y,int z,int v){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z,cost[tot]=v;}
void insert(int x,int y,int z,int v,bool flag=1){
if (!flag) swap(x,y);
join(x,y,z,v),join(y,x,0,-v);
}
int G(int x,int y){return (x-1)*m+y;}
bool in_map(int x,int y){return x>0&&x<=n&&y>0&&y<=m;}
bool SPFA(){
memset(dis,63,sizeof(dis));
memset(deep,255,sizeof(deep));
static int h[N+10];
static bool vis[N+10];
int head=0,tail=1;
h[1]=S,vis[S]=1,dis[S]=deep[S]=0;
while (head!=tail){
if (++head>N) head=1;
int Now=h[head];
for (int p=now[Now],son=child[p];p;p=pre[p],son=child[p]){
if (val[p]>0&&dis[son]>dis[Now]+cost[p]){
dis[son]=dis[Now]+cost[p];
deep[son]=deep[Now]+1;
if (!vis[son]){
if (++tail>N) tail=1;
vis[h[tail]=son]=1;
}
}
}
vis[Now]=0;
}
return ~deep[T];
}
int dfs(int x,int v){
if (x==T) return v;
int res=0;
for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
if (val[p]>0&&deep[son]>deep[x]&&dis[son]==dis[x]+cost[p]){
int k=dfs(son,min(v,val[p]));
res+=k,v-=k;
val[p]-=k,val[p^1]+=k;
if (!v) break;
}
}
if (!res) deep[x]=-1;
return res;
}
int MCMF(int Ans_Flow){
int Max_Flow=0,Min_cost=0;
while (SPFA()){
int res=dfs(S,inf);
Max_Flow+=res;
Min_cost+=res*dis[T];
}
return Max_Flow==Ans_Flow?Min_cost:-1;
}
int main(){
static int g[16]; g[0]=0;
for (int i=1;i<16;i++) g[i]=g[i-lowbit(i)]+1;
n=read(),m=read(),All=n*m,S=All*5+1,T=S+1;
int Ans_Flow1=0,Ans_Flow2=0;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
int x=read();
if ((i+j)&1){
insert(S,G(i,j),inf,0);
Ans_Flow1+=g[x];
for (int k=0;k<4;k++){
int tx=i+dx[k],ty=j+dy[k];
if (!in_map(tx,ty)) continue;
insert(All*(k+1)+G(i,j),All*((k+2)%4+1)+G(tx,ty),1,0);
}
}else{
insert(G(i,j),T,inf,0);
Ans_Flow2+=g[x];
}
if (!g[x]) continue;
if (g[x]==1){
for (int k=0;k<4;k++){
if (x>>k&1){
insert(G(i,j),All*(k+1)+G(i,j),1,0,(i+j)&1);
insert(All*(k+1)+G(i,j),All*((k+1)%4+1)+G(i,j),inf,1,(i+j)&1);
insert(All*(k+1)+G(i,j),All*((k+2)%4+1)+G(i,j),inf,2,(i+j)&1);
insert(All*(k+1)+G(i,j),All*((k+3)%4+1)+G(i,j),inf,1,(i+j)&1);
break;
}
}
}
if (g[x]==2){
int ID_1=-1,ID_2=-1;
for (int k=0;k<4;k++) if (x>>k&1) !~ID_1?ID_1=k:ID_2=k;
insert(G(i,j),All*(ID_1+1)+G(i,j),1,0,(i+j)&1);
insert(G(i,j),All*(ID_2+1)+G(i,j),1,0,(i+j)&1);
if (ID_2-ID_1==2) continue;
insert(All*(ID_1+1)+G(i,j),All*((ID_1+2)%4+1)+G(i,j),inf,1,(i+j)&1);
insert(All*(ID_2+1)+G(i,j),All*((ID_2+2)%4+1)+G(i,j),inf,1,(i+j)&1);
}
if (g[x]==3){
for (int k=0;k<4;k++){
if (!(x>>k&1)){
insert(G(i,j),All*((k+1)%4+1)+G(i,j),1,0,(i+j)&1);
insert(G(i,j),All*((k+2)%4+1)+G(i,j),1,0,(i+j)&1);
insert(G(i,j),All*((k+3)%4+1)+G(i,j),1,0,(i+j)&1);
insert(All*((k+1)%4+1)+G(i,j),All*(k+1)+G(i,j),inf,1,(i+j)&1);
insert(All*((k+2)%4+1)+G(i,j),All*(k+1)+G(i,j),inf,2,(i+j)&1);
insert(All*((k+3)%4+1)+G(i,j),All*(k+1)+G(i,j),inf,1,(i+j)&1);
break;
}
}
}
if (g[x]==4)
for (int k=0;k<4;k++)
insert(G(i,j),All*(k+1)+G(i,j),inf,0,(i+j)&1);
}
}
int Ans_Flow=max(Ans_Flow1,Ans_Flow2);
printf("%d\n",MCMF(Ans_Flow));
return 0;
}