核心思想:在最小割中用容量为无穷大的边来表示限制。
这题相当于一个
p∗q
的矩阵,每个矩阵添一个
[0,r]
的数,且相邻数之差不超过
d
,同时最小化
先把一个格子的取值串起来,就是
(i,j,k)
向
(i,j,k+1)
连一条容量为
v(i,j,k+1)
的边;
特别地,
S
向
那么这些边中必须割一条,表示填的数是什么。
然后考虑差不超过d的限制,就是
(i,j,k)
向
(i−1,j,k−d)
、
(i+1,j,k−d)
、
(i,j−1,k−d)
、
(i,j+1,k−d)
连一条容量为无穷大的边。
这样就保证不能同时割
(i,j,≥k)
和
(相邻的,≤k−d)
;因为每串最多割一条边。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define G(x,y,z) (((x-1)*m+y-1)*r+z-1)
using namespace std;
int n,m,r,d,v[45][45][45],S,T,dl[64010],ne[64010];
bool vis[64010];
struct edge
{
int t,c,f;
edge *next,*rev;
}*con[64010];
void ins(int x,int y,int c)
{
edge *p=new edge;p->t=y;p->c=c;p->f=0;p->next=con[x];con[x]=p;
p=new edge;p->t=x;p->c=0;p->f=0;p->next=con[y];con[y]=p;
con[x]->rev=con[y];con[y]->rev=con[x];
}
bool bfs()
{
bool re=0;
memset(ne,0,sizeof(ne));
ne[S]=1;dl[1]=S;
for(int head=1,tail=1;head<=tail;head++)
{
int v=dl[head];
if(v==T) re=1;
for(edge *p=con[v];p;p=p->next)
if(p->c>p->f&&ne[p->t]==0)
ne[p->t]=ne[v]+1,dl[++tail]=p->t;
}
return re;
}
int dinic(int v,int flow)
{
if(v==T) return flow;
if(vis[v]) return 0;
int re=0;
for(edge *p=con[v];p;p=p->next)
if(p->c>p->f&&ne[p->t]==ne[v]+1)
{
int tmp=dinic(p->t,min(flow,p->c-p->f));
re+=tmp;p->f+=tmp;
flow-=tmp;p->rev->f-=tmp;
if(flow==0) break;
}
if(re==0) vis[v]=1;
return re;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&r,&d);
for(int i=1;i<=r;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=m;k++)
scanf("%d",&v[j][k][i]);
S=n*m*r;T=n*m*r+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
for(int k=1;k<=r;k++)
{
ins(k==1?S:G(i,j,k-1),G(i,j,k),v[i][j][k]);
if(k-d>0)
{
if(i>1) ins(G(i,j,k),G(i-1,j,k-d),0x3f3f3f3f);
if(i<n) ins(G(i,j,k),G(i+1,j,k-d),0x3f3f3f3f);
if(j>1) ins(G(i,j,k),G(i,j-1,k-d),0x3f3f3f3f);
if(j<m) ins(G(i,j,k),G(i,j+1,k-d),0x3f3f3f3f);
}
}
ins(G(i,j,r),T,0x3f3f3f3f);
}
int ans=0;
while(bfs())
{
memset(vis,0,sizeof(vis));
ans+=dinic(S,0x3f3f3f3f);
}
printf("%d",ans);
return 0;
}