标签:src span 而不是 print set 不难 main 技术 表示
首先将棋盘黑白染色,要使得棋盘不漏水,当且仅当每个黑点的各个方向的管口都连接上了一个白点的管口。换句话说,我们要让黑点和白点匹配数最大的同时操作次数最小,不难想到最小费用最大流。
对于一条边的描述\((w,c)\)表示容量为\(w\),费用为\(c\)。
我们将每个点拆成上下左右四个点,从源点向黑点的所有存在管口的方向连\((1,0)\)的边,白点所有存在管口的方向向汇点连\((1,0)\)的边。
之后对于每对相邻的点,从黑点的一个管口向白点对应管口连\((1,0)\)的边。比如\((i,j)\)和\((i-1,j)\),\((i,j)\)是黑点,我们从\((i,j)\)的上方向向\((i-1,j)\)的下方向连边。
现在考虑怎么处理旋转,我们不妨分类讨论。
定义\((i,j,0/1/2/3)\)分别表示\((i,j)\)的表示上下左右的点。
1.只有一个管口的:
以下图为例,其他同理:
顺时针旋转\(90\)度:
此时管口从上移到了右。
如果这是个黑点,那么从\((i,j,0)\)向\((i,j,3)\)连\((1,1)\)的边,这表示从\(S\)流向\((i,j,0)\)的容量为\(1\)的流可以通过这条边改变方向,与\((i,j+1,2)\)匹配,而不是\((i-1,j,1)\)。
如果这是个白点,我们从从\((i,j,3)\)向\((i,j,0)\)连\((1,1)\)的边,这表示\((i,j,3)\)可以匹配后通过这条边对汇点贡献流量。
下面所有默认\((i,j)\)是黑点。(是白点就将起点和终点反过来)
逆时针旋转\(90\)度:
同理,连边\(((i,j,0),(i,j,2),1,1,)\)
旋转\(180\)度:
同理,连边\(((i,j,0),(i,j,1),1,1)\)。
2.两个管口的:
因为直的禁止旋转,我们只需要考虑\(L\)型的即可。
仍只以下图为例,其他同理:
顺时针旋转\(90\)度:
此时向右的管口没有消失,而向上的变为了向下的,因此从\((i,j,0)\)向\((i,j,1)\)连\((1,1)\)的边。
逆时针旋转\(90\)度:
此时向上的管口没有消失,而向右的变为了向左的,因此从\((i,j,3)\)向\((i,j,2)\)连\((1,1)\)的边。
旋转\(180\)度:
这时管从上右变为了左下,费用为\(2\),我们发现我们之前连的两条边正好产生了这个作用。
3.三个管口的
仍然以下图为例。
上面两个要是理解了,这个就很快知道了,于是只给出连边。
顺时针旋转\(90\)度:
从\((i,j,2)\)向\((i,j,1)\)连\((1,1)\)的边。
逆时针旋转\(90\)度:
从\((i,j,3)\)向\((i,j,1)\)连\((1,1)\)的边。
旋转\(180\)度:
从\((i,j,0)\)向\((i,j,1)\)连\((1,2)\)的边。
code:
#include<bits/stdc++.h>
using namespace std;
#define re register
#define pii pair<int,int>
#define mkp make_pair
#define fir first
#define sec second
const int maxn=2010;
const int inf=1e9;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
int n,m,tot=1,cnt_edge=1,S,T,sum;
int head[maxn*5],dis[maxn*5],col[maxn*5];
int a[maxn][maxn];
int id[maxn][maxn][5];//0->up,1->down,2->left,3->right.
bool vis[maxn*5];
struct edge{int to,nxt,flow,cost;}e[maxn*50];
inline int read()
{
char c=getchar();re int res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
inline int change(int k)
{
re int op=2333;
if(k==0)op=0;
if(k==1)op=3;
if(k==2)op=1;
if(k==3)op=2;
return op;
}
inline void add(int u,int v,int w,int c)
{
e[++cnt_edge].nxt=head[u];
head[u]=cnt_edge;
e[cnt_edge].to=v;
e[cnt_edge].flow=w;
e[cnt_edge].cost=c;
}
inline void addflow(int u,int v,int w,int c)
{
if(!col[u]&&u!=S&&v!=T)swap(u,v);//edge:: black->white.
add(u,v,w,c);add(v,u,0,-c);
}
inline void solve(int x,int y,int type)
{
if(!type||type==5||type==10||type==15)return;
int num=0;
for(int i=0;i<4;i++)if((type>>i)&1)num++;
if(num==1)
{
if(type==1)
{
re int turn=0;
addflow(id[x][y][turn],id[x][y][3],1,1);
addflow(id[x][y][turn],id[x][y][2],1,1);
addflow(id[x][y][turn],id[x][y][1],1,2);
return;
}
if(type==2)
{
re int turn=3;
addflow(id[x][y][turn],id[x][y][1],1,1);
addflow(id[x][y][turn],id[x][y][0],1,1);
addflow(id[x][y][turn],id[x][y][2],1,2);
return;
}
if(type==4)
{
re int turn=1;
addflow(id[x][y][turn],id[x][y][2],1,1);
addflow(id[x][y][turn],id[x][y][3],1,1);
addflow(id[x][y][turn],id[x][y][0],1,2);
return;
}
if(type==8)
{
re int turn=2;
addflow(id[x][y][turn],id[x][y][0],1,1);
addflow(id[x][y][turn],id[x][y][1],1,1);
addflow(id[x][y][turn],id[x][y][3],1,2);
return;
}
}
if(num==2)
{
if(type==3)
{
addflow(id[x][y][0],id[x][y][1],1,1);
addflow(id[x][y][3],id[x][y][2],1,1);
return;
}
if(type==6)
{
addflow(id[x][y][3],id[x][y][2],1,1);
addflow(id[x][y][1],id[x][y][0],1,1);
return;
}
if(type==9)
{
addflow(id[x][y][2],id[x][y][3],1,1);
addflow(id[x][y][0],id[x][y][1],1,1);
return;
}
if(type==12)
{
addflow(id[x][y][1],id[x][y][0],1,1);
addflow(id[x][y][2],id[x][y][3],1,1);
return;
}
}
if(num==3)
{
if(type==7)
{
addflow(id[x][y][0],id[x][y][2],1,1);
addflow(id[x][y][1],id[x][y][2],1,1);
addflow(id[x][y][3],id[x][y][2],1,2);
return;
}
if(type==11)
{
addflow(id[x][y][2],id[x][y][1],1,1);
addflow(id[x][y][3],id[x][y][1],1,1);
addflow(id[x][y][0],id[x][y][1],1,2);
return;
}
if(type==13)
{
addflow(id[x][y][1],id[x][y][3],1,1);
addflow(id[x][y][0],id[x][y][3],1,1);
addflow(id[x][y][2],id[x][y][3],1,2);
return;
}
if(type==14)
{
addflow(id[x][y][3],id[x][y][0],1,1);
addflow(id[x][y][2],id[x][y][0],1,1);
addflow(id[x][y][1],id[x][y][0],1,2);
return;
}
}
}
inline bool spfa()
{
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
deque<int>q;
q.push_front(S);dis[S]=0;vis[S]=1;
while(!q.empty())
{
re int x=q.front();q.pop_front();vis[x]=0;
for(re int i=head[x];i;i=e[i].nxt)
{
re int y=e[i].to;
if(dis[y]>dis[x]+e[i].cost&&e[i].flow>0)
{
dis[y]=dis[x]+e[i].cost;
if(!vis[y])
{
if(q.empty()||dis[y]>dis[q.front()])q.push_back(y);
else q.push_front(y);
vis[y]=1;
}
}
}
}
return dis[T]!=0x3f3f3f3f;
}
int dfs(int x,int lim)
{
vis[x]=1;
if(x==T||lim<=0)return lim;
re int res=lim;
for(re int i=head[x];i;i=e[i].nxt)
{
re int y=e[i].to;
if(dis[y]!=dis[x]+e[i].cost||e[i].flow<=0||vis[y])continue;
re int tmp=dfs(y,min(res,e[i].flow));
res-=tmp;
e[i].flow-=tmp,e[i^1].flow+=tmp;
if(res<=0)break;
}
return lim-res;
}
inline pii Dinic()
{
re int res=0,cost=0;
while(spfa())
{
re int flow=dfs(S,inf);
while(flow>0)
{
res+=flow,cost+=flow*dis[T];
memset(vis,0,sizeof(vis));
flow=dfs(S,inf);
}
}
return mkp(res,cost);
}
int main()
{
n=read(),m=read();
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
a[i][j]=read();
S=1;
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
for(re int k=0;k<4;k++)
{
id[i][j][k]=++tot;
col[tot]=!((i+j)&1);
}
T=tot+1;
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
for(re int k=0;k<4;k++)
{
if(!((a[i][j]>>k)&1))continue;
sum++;
re int op=change(k);
if(!((i+j)&1))addflow(S,id[i][j][op],1,0);
else addflow(id[i][j][op],T,1,0);
}
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
{
if(i<n)addflow(id[i][j][1],id[i+1][j][0],1,0);
if(j<m)addflow(id[i][j][3],id[i][j+1][2],1,0);
}
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
solve(i,j,a[i][j]);
pii ans=Dinic();
printf("%d",ans.fir==(sum>>1)?ans.sec:-1);
return 0;
}
标签:src span 而不是 print set 不难 main 技术 表示
原文地址:https://www.cnblogs.com/nofind/p/12111701.html