[BZOJ5120]无限之环

Description
曾经有一款流行的游戏,叫做InfinityLoop,先来简单的介绍一下这个游戏:
游戏在一个n×m的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中点有接口,所有水管的粗细都相同,所以如果两个相邻方格的公共边界的中点都有接头,那么可以看作这两个接头互相连接。水管有以下15种形状:
vv2.png
游戏开始时,棋盘中水管可能存在漏水的地方。
形式化地:如果存在某个接头,没有和其它接头相连接,那么它就是一个漏水的地方。
玩家可以进行一种操作:选定一个含有非直线型水管的方格,将其中的水管绕方格中心顺时针或逆时针旋转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棋盘如下
vv1.png
旋转方法很显然,先将左上角虚线方格内的水管顺时针转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了……

1214431-20190217215112987-930844900.png

/*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;
}

转载于:https://www.cnblogs.com/Wolfycz/p/10392867.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值