LOJ2321「清华集训 2017」无限之环

13 篇文章 0 订阅
4 篇文章 0 订阅

原题链接:https://loj.ac/problem/2321

无限之环

题目描述

曾经有一款流行的游戏,叫做 InfinityLoop I n f i n i t y L o o p ,先来简单的介绍一下这个游戏:

游戏在一个 n×m n × m 的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在方格某些方向的边界的中点有接口,所有水管的粗细都相同,所以如果两个相邻方格的公共边界的中点都有接头,那么可以看作这两个接头互相连接。水管有以下 15 15 种形状:

1.png

游戏开始时,棋盘中水管可能存在漏水的地方。

形式化地:如果存在某个接头,没有和其它接头相连接,那么它就是一个漏水的地方。

玩家可以进行一种操作:选定一个含有非直线型水管的方格,将其中的水管绕方格中心顺时针或逆时针旋转 90 90 度。

直线型水管是指左图里中间一行的两种水管。

现给出一个初始局面,请问最少进行多少次操作可以使棋盘上不存在漏水的地方。

输入格式

第一行两个正整数 n,m n , m ,代表网格的大小。

接下来 n n 行每行 m 个数,每个数是 [0,15] [ 0 , 15 ] 中的一个,你可以将其看作一个 4 4 位的二进制数,从低到高每一位分别代表初始局面中这个格子上、右、下、左方向上是否有水管接头。

特别地,如果这个数是 0,则意味着这个位置没有水管。

比如 3(0011(2)) 3 ( 0011 ( 2 ) ) 代表上和右有接头,也就是一个 L 型,而 12(1100(2)) 12 ( 1100 ( 2 ) ) 代表下和左有接头,也就是将 L 型旋转 180 180 度。

输出格式

输出共一行,表示最少操作次数。如果无法达成目标,输出 1 − 1 .

样例
样例输入 1

2 3
3 14 12
3 11 12

样例输出 1

2

样例输入 2

3 2
1 8
5 10
2 4

样例输出 2

-1

样例输入 3

3 3
9 11 3
13 15 7
12 14 6

样例输出 3

16

题解

著名的网络流毒瘤好题。

如果能顺切这道题,网络流水平已经出神入化了。

先考虑不转的情况,我们需要判断的就是当前的网络是否“漏水”。怎么样才叫不漏水呢?按照题目要求,每个接头都要与其他的一个接头相连,即是说,每个接头都要满流。

既然每个格子都要与上下左右的四个格子连接并保证满流,我们就会想到黑白染色的技巧,将相邻格子之间建立起联系,并分别连接超级源点、超级汇点。不妨将每个格子拆成上下左右四个点,强制黑格子的对应点向相邻白格子对应点连边,每个格子内部根据管子的初始状态连边。

将上述格子与格子之间、格子内部的边的容量都设为 1 1 ,超级源点汇点都连inf,跑一遍最大流,查看格点内部的边是否满流就能知道有没有“漏水”了。

这道题要求的是转动次数,我们可以理解为“费用”,下面以黑色格子为例,冷静分析一下(白色的反着连就好了):

考虑度数为 1 1 的管子:

1.png

顺时针旋转π2

1.png

发现对于一个旋转操作,相当于从 B B C连了一条容量为 1 1 ,费用为1边。

而旋转 π π 就相当于从 B B D连了一条容量为 1 1 ,费用为2边。

类比一下,我们就能得到其他管子的连边。

度数为 2 2 时:

2.png

顺时针旋转π2时:

cap(AC)=1,cost(AC)=1 c a p ( A → C ) = 1 , c o s t ( A → C ) = 1

同理,逆时针旋转 π2 π 2 时:

cap(BD)=1,cost(BD)=1 c a p ( B → D ) = 1 , c o s t ( B → D ) = 1

而旋转 π π 时就相当于上面两个操作加和。

度数为 3 3 时:

3.png

顺时针旋转π2时:

cap(AD)=1,cost(AD)=1 c a p ( A → D ) = 1 , c o s t ( A → D ) = 1

同理,逆时针旋转 π2 π 2 时:

cap(CD)=1,cost(CD)=1 c a p ( C → D ) = 1 , c o s t ( C → D ) = 1

旋转 π π 时:

cap(BD)=1,cost(BD)=2 c a p ( B → D ) = 1 , c o s t ( B → D ) = 2

这样也很好理解为什么不资瓷旋转直线型了,因为直线只有一种旋转方式不能加和,并且一次更改了两个接口,没办法用上面的方法建图。

连完边跑费用流就好了,打板贼 6 6 <script type="math/tex" id="MathJax-Element-60">6</script>。

代码

看到第一行代码,大家应该知道我经历了什么。。。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int M=25000,N=1e5,G=2005;
struct sd{int to,fl,val;}ed[M];
struct cd{int v,w,a,s,d;}pt[N];
int n,m,tot,id,totf,maxf,cost,start,end,edge[9],dis[N],minf[N],pre[N],val[G][G];
bool vis[N];
vector<int>mmp[N];
deque<int>dui;
void add(int f,int t,int fl,int val,int typ)
{
    mmp[f].push_back(id);ed[id++]=(sd){t,(typ?fl:0),(typ?val:-val)};
    mmp[t].push_back(id);ed[id++]=(sd){f,(typ?0:fl),(typ?-val:val)};
}
void link(int x,int y,int val)
{
    if(!val)return;
    int pos=(x-1)*m+y,w,a,s,d,v,du=0,p,typ=(x+y)%2;
    memset(edge,0,sizeof(edge));
    for(int i=0;i<=3;++i){p=1<<i;if(val&p)edge[i]=1,++du;}
    w=pt[pos].w;a=pt[pos].a;s=pt[pos].s;d=pt[pos].d;
    if(typ)
    {
        if(x>1)add(w,pt[pos-m].s,1,0,1);if(x<n)add(s,pt[pos+m].w,1,0,1);
        if(y>1)add(a,pt[pos-1].d,1,0,1);if(y<m)add(d,pt[pos+1].a,1,0,1);
        if(edge[0])add(start,w,1,0,1),++totf;if(edge[1])add(start,d,1,0,1),++totf;
        if(edge[2])add(start,s,1,0,1),++totf;if(edge[3])add(start,a,1,0,1),++totf;
    }
    else
    {
        if(edge[0])add(w,end,1,0,1),++totf;if(edge[1])add(d,end,1,0,1),++totf;
        if(edge[2])add(s,end,1,0,1),++totf;if(edge[3])add(a,end,1,0,1),++totf;
    }
    if(du==1)
    {
        if(edge[0])add(w,a,1,1,typ),add(w,d,1,1,typ),add(w,s,1,2,typ);
        else if(edge[1])add(d,w,1,1,typ),add(d,s,1,1,typ),add(d,a,1,2,typ);
        else if(edge[2])add(s,a,1,1,typ),add(s,d,1,1,typ),add(s,w,1,2,typ);
        else if(edge[3])add(a,s,1,1,typ),add(a,w,1,1,typ),add(a,d,1,2,typ);
    }
    else if(du==2)
    {
        if((edge[0]&edge[2])||(edge[1]&edge[3]))return;
        if(edge[0])add(w,s,1,1,typ);if(edge[1])add(d,a,1,1,typ);
        if(edge[2])add(s,w,1,1,typ);if(edge[3])add(a,d,1,1,typ);
    }
    else if(du==3)
    {
        if(!edge[0])add(a,w,1,1,typ),add(d,w,1,1,typ),add(s,w,1,2,typ);
        else if(!edge[1])add(w,d,1,1,typ),add(s,d,1,1,typ),add(a,d,1,2,typ);
        else if(!edge[2])add(a,s,1,1,typ),add(d,s,1,1,typ),add(w,s,1,2,typ);
        else if(!edge[3])add(w,a,1,1,typ),add(s,a,1,1,typ),add(d,a,1,2,typ);
    }
}
void build()
{
    int pos;
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)
    {
        if(!val[i][j])continue;
        pos=(i-1)*m+j,pt[pos].w=++tot,pt[pos].a=++tot,pt[pos].s=++tot,pt[pos].d=++tot;
    }
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)link(i,j,val[i][j]);
}
void in()
{
    int a;
    scanf("%d%d",&n,&m);start=n*m*5+100,end=n*m*5+101;
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&val[i][j]);
    build();
}
bool spfa(int s,int e)
{
    fill(dis,dis+1+tot,INT_MAX);dis[end]=INT_MAX;
    memset(vis,0,sizeof(vis));
    dui.push_front(s);vis[s]=1;dis[s]=0;minf[s]=inf;
    int f,hh,to,fl,val;
    while(!dui.empty())
    {
        f=dui.front();dui.pop_front();vis[f]=0;
        for(int i=mmp[f].size()-1;i>=0;--i)
        {
            hh=mmp[f][i];to=ed[hh].to;fl=ed[hh].fl;val=ed[hh].val;
            if(fl>0&&dis[to]>dis[f]+val)
            {
                dis[to]=dis[f]+val;minf[to]=min(minf[f],fl);pre[to]=hh;
                if(!vis[to])dui.push_back(to),vis[to]=1;
            }
        }
    }
    return dis[e]!=INT_MAX;
}
void up(int s,int e)
{
    int v=e,hh;
    while(v!=s){hh=pre[v];ed[hh].fl-=minf[e];ed[hh^1].fl+=minf[e];v=ed[hh^1].to;}
    maxf+=minf[e];cost+=minf[e]*dis[e];
}
void ac()
{
    while(spfa(start,end))up(start,end);
    printf("%d",maxf*2==totf?cost:-1);
}
int main(){in();ac();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值