无限之环

一、题目

点此看题

二、解法

真的是神题,我肝了半天才肝出来。

我们应该如何理解这个使棋盘上不存在漏水的地方的含义?可以把这个矩阵黑白染色,让源点连白点,黑点连汇点,所求得的最大流 × 2 \times 2 ×2需要等于接头的个数。本题要求操作次数最小,就可以理解为最小费用最大流。

对于矩阵中的每个点我们建 5 5 5个点,分别表示四个方向的接头和控制流量的中心点,难点在于旋转如何建图,我们考虑其内部的变化。

情况1:只有一处接口

   A
   |
 D O B
   C

如图,我们可以建三条边,分别是:
A − B , A − C , A − D A-B,A-C,A-D AB,AC,AD,权值为 1 1 1,容量为 1 1 1,接在 A A A上可以限流。

情况2:有两处接口且水管非直线

   A
   |
 D O --B
   C

如图,我们可以建两条边,分别是:
A − C , B − D A-C,B-D AC,BD,权值为 1 1 1,容量为 1 1 1,为了理解它的正确性,我们可以分类讨论:

  • 顺时针旋转 90 90 90,用到了 A − C A-C AC这条边,花费正好为 1 1 1
  • 逆时针旋转 90 90 90,用到了 B − D B-D BD这条边,花费正好为 1 1 1
  • 顺时针旋转 180 180 180,同时用到了 A − C , B − D A-C,B-D AC,BD,花费为 2 2 2

情况3:有三处接口

  A
  |
D O --B
  |
  C

如图,我们可以见三条边,分别是:
A − D ( 1 , 1 ) , C − D ( 1 , 1 ) , B − D ( 2 , 1 ) A-D(1,1),C-D(1,1),B-D(2,1) AD(1,1)CD(1,1),BD(2,1)(前者为费用,后者为流量。)

其他情况我们就不需要旋转了,这里有几个细节,对于实现此题很重要:

  • 要弄清楚顺序,本题一定要让白点连黑点。
  • 0 , 1 , 2 , 3 0,1,2,3 0,1,2,3代表上右下左,对于每个点向外连边是要用这样的顺序。
  • 即使一个点没有接口也要给四个方向编号,要不然建边会出问题。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <queue>
using namespace std;
#define inf 0x3f3f3f3f
const int MAXN = 10005;
int read()
{
    int num=0,flag=1;
    char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int n,m,tot=1,sum,cnt,S,T,f[MAXN],bit[20],num[MAXN][5];
int flow[MAXN],dis[MAXN],pre[MAXN],lst[MAXN];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};//上右下左
struct edge
{
    int v,c,f,next;
}e[MAXN*100];
struct node
{
    int u,c;
};
queue<node> q;
void add_edge(int u,int v,int c,int fl,int ty=1)
{
    if(!ty) swap(u,v);
    e[++tot]=edge{v,c,fl,f[u]},f[u]=tot;
    e[++tot]=edge{u,-c,0,f[v]},f[v]=tot;
}
bool bfs()
{
    memset(dis,0x3f,sizeof dis);
    flow[S]=inf;
    dis[S]=0;
    pre[S]=-1;
    q.push(node{S,0});
    while(!q.empty())
    {
        int u=q.front().u,t=q.front().c;
        q.pop();
        if(dis[u]<t) continue;
        for(int i=f[u]; i; i=e[i].next)
        {
            int v=e[i].v,c=e[i].c;
            if(dis[v]>dis[u]+c && e[i].f>0)
            {
                dis[v]=dis[u]+c;
                pre[v]=u;
                lst[v]=i;
                flow[v]=min(flow[u],e[i].f);
                q.push(node{v,dis[v]});
            }
        }
    }
    return dis[T]!=inf;
}
void add(int x,int y,int c)
{
    sum+=bit[c];
    int z=(x-1)*m+y,ty=(x+y)&1;
    for(int i=0;i<=4;i++)
        num[z][i]=++cnt;
    if(bit[c]==0) return;
    if(ty) add_edge(S,num[z][4],0,bit[c]);
    else add_edge(num[z][4],T,0,bit[c]);
    for(int i=0;i<4;i++)
        if(c&(1<<i))
            add_edge(num[z][4],num[z][i],0,1,ty);
    if(bit[c]==1)
    {
        for(int i=0;i<4;i++)
            if(c&(1<<i))
            {
                add_edge(num[z][i],num[z][(i+1)%4],1,1,ty);
                add_edge(num[z][i],num[z][(i+2)%4],2,1,ty);
                add_edge(num[z][i],num[z][(i+3)%4],1,1,ty);
            }
    }
    if(bit[c]==2)
    {
        for(int i=0;i<4;i++)
            if(c&(1<<i) && c&(1<<((i+2)%4)))
                return ;
        for(int i=0;i<4;i++)
            if(c&(1<<i))
                add_edge(num[z][i],num[z][(i+2)%4],1,1,ty);
    }
    if(bit[c]==3)
    {
        int t=0;
        for(int i=0;i<4;i++)
            if(!(c&(1<<i)))
                t=i;
        for(int i=0;i<4;i++)
            if(c&(1<<i))
            {
                if(i==(t+2)%4) add_edge(num[z][i],num[z][t],2,1,ty);
                else add_edge(num[z][i],num[z][t],1,1,ty);
            }
    }
}
void get()
{
    int res=0,cost=0;
    while(bfs())
    {
        res+=flow[T];
        cost+=dis[T]*flow[T];
        int cur=T;
        while(cur!=S)
        {
            e[lst[cur]].f-=flow[T];
            e[lst[cur]^1].f+=flow[T];
            cur=pre[cur];
        }
    }
    if(res*2!=sum) puts("-1");
    else printf("%d\n",cost);
}
int main()
{
    n=read();m=read();
    S=++cnt;T=++cnt;
    for(int i=1;i<=15;i++)
        bit[i]=bit[i>>1]+(i&1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            add(i,j,read());
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if((i+j)&1)
            for(int k=0;k<4;k++)
            {
                int tx=i+dx[k],ty=j+dy[k];
                if(tx>=1 && tx<=n && ty>=1 && ty<=m)
                    add_edge(num[(i-1)*m+j][k],num[(tx-1)*m+ty][(k+2)%4],0,1);
            }
    get();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你可以使用CSS3的动画来实现无限旋转的轮播图。下面是一个简单的示例代码: HTML: ``` <div class="carousel"> <div class="slide"> <img src="image1.jpg"> </div> <div class="slide"> <img src="image2.jpg"> </div> <div class="slide"> <img src="image3.jpg"> </div> </div> ``` CSS: ``` .carousel { width: 300px; height: 300px; overflow: hidden; position: relative; } .slide { width: 300px; height: 300px; position: absolute; top: 0; left: 0; animation: carousel 10s linear infinite; } .slide:nth-child(1) { transform: rotateY(0deg) translateZ(150px); } .slide:nth-child(2) { transform: rotateY(120deg) translateZ(150px); } .slide:nth-child(3) { transform: rotateY(240deg) translateZ(150px); } @keyframes carousel { from { transform: rotateY(0deg); } to { transform: rotateY(360deg); } } ``` 上述代码中,我们使用了CSS3的3D变换和动画来实现轮播图的旋转效果。其中,`.carousel`元素作为轮播容器,使用`overflow: hidden`隐藏溢出部分,`.slide`元素表示每个轮播项,使用`position: absolute`绝对定位并使用`transform`属性设置旋转和平移效果。`animation`属性指定了轮播动画的名称、持续时间、时间函数和重复次数。`@keyframes`指定了动画的关键帧,从0度旋转到360度实现旋转效果。`nth-child`选择器则用于设置每个轮播项的位置和旋转角度。 你可以根据自己的需求调整轮播容器和轮播项的大小、数量和位置,以实现不同的效果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值