Description
Input
第一行是三个正整数P,Q,R,表示切糕的长P、 宽Q、高R。第二行有一个非负整数D,表示光滑性要求。接下来是R个P行Q列的矩阵,第z个 矩阵的第x行第y列是v(x,y,z) (1≤x≤P, 1≤y≤Q, 1≤z≤R)。
100%的数据满足P,Q,R≤40,0≤D≤R,且给出的所有的不和谐值不超过1000。
Output
仅包含一个整数,表示在合法基础上最小的总不和谐值。
Sample Input
2 2 2
1
6 1
6 1
2 6
2 6
Sample Output
6
HINT
最佳切面的f为f(1,1)=f(2,1)=2,f(1,2)=f(2,2)=1
我们建一个r+1层的,每层都是p*q的图,同时建两个空点S和T,表示源点和汇点。从源点S连到第一层的所有边都赋值为2e8,第r+1层到汇点T的边都赋值为2e8。而点(x,y,z)向点(x,y,z+1)连一条权值为v(x,y)的边,这样我们就把这个题目构筑成一个网络流图了。
那我们先想:如果没有题目里对切割点距离的要求,那我们直接求出这张图的最大流即可。(因为该题很明显是求这张图的最小割,而根据最大流最小割定理,最大流=最小割)
那我们再想:当有距离限制时,做法也是一样的,我们依旧只需要求出这张图的最小割即可,但是在求之前,我们要先连一些特殊的边。
特殊的边:对于点(x,y,z),我们要连上(x,y,z)和(x1,y1,z+d),并赋值为2e8(其中x1,y1表示与(x,y)临近的点,具体来说就是(x,y-1)(x,y+1)(x-1,y)(x+1,y)这四个点)
那么我们为什么要连这些边呢?这里做个证明:
如图所示,这是一个d为1的图,绿色的边在红色的边下面两格,所以在这张图里不可以同时选红色和绿色。我们根据最小割的定义,最小割必须把这张图分割成两个点集。如图我们连过边后,若选用红色和绿色,我们发现割去后整张图依旧在一个点集里,所以不可行。而如果我们选择红色和紫色的边,会发现这两条边将会将整张图割为两个点集,满足最小割的定义。红边和紫边的间隔只有一格,也满足题目里的距离限制。
我们通过这个小tips就可以解决这道题了。
Code:
#include<bits/stdc++.h>
using namespace std;
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
const int maxl=45,maxn=75000,maxm=maxn<<4,inf=2e8,mov[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int id[maxl][maxl][maxl],head[maxn],nxt[maxm],Q[maxm],dis[maxn];
int q,p,r,d,ans,T,S,cnt,num,h,t,tot,xx;
struct node{
int to,val;
}L[maxm];
void add(int x,int y,int val){
L[cnt]=(node){y,val};
nxt[cnt]=head[x];head[x]=cnt;cnt++;
L[cnt]=(node){x,0};
nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
int BFS(){
memset(dis,0,sizeof(dis));
dis[S]=1;h=0;t=1;Q[t]=S;
while(h<t){
int front=Q[++h];
for(int i=head[front];i!=-1;i=nxt[i]){
int to=L[i].to;
if(!dis[to]&&L[i].val){
dis[to]=dis[front]+1;
Q[++t]=to;
}
}
}
return dis[T];
}
int DFS(int now,int x){
if(now==T) return x;
int res=0;
for(int i=head[now];i!=-1&&x;i=nxt[i]){
int to=L[i].to;
if(dis[to]==dis[now]+1&&L[i].val){
int fd=DFS(to,min(x,L[i].val));
res+=fd;x-=fd;
L[i].val-=fd;L[i^1].val+=fd;
}
}
if(!res) dis[now]=0;
return res;
}
int main()
{
memset(head,-1,sizeof(head));
p=read();q=read();r=read();d=read();tot=0;
for(int i=1;i<=r+1;i++)
for(int j=1;j<=p;j++)
for(int k=1;k<=q;k++) id[i][j][k]=++tot;
S=0;T=++tot;
for(int i=1;i<=r;i++)
for(int j=1;j<=p;j++)
for(int k=1;k<=q;k++)
xx=read(),add(id[i][j][k],id[i+1][j][k],xx);
for(int i=1;i<=p;i++)
for(int j=1;j<=q;j++) add(S,id[1][i][j],inf),add(id[r+1][i][j],T,inf);
#define nx (j+mov[mv][0])
#define ny (k+mov[mv][1])
for(int i=d+1;i<=r+1;i++)
for(int j=1;j<=p;j++)
for(int k=1;k<=q;k++)
for(int mv=0;mv<4;mv++)
if(id[i][nx][ny]) add(id[i][j][k],id[i-d][nx][ny],inf);
#undef nx
#undef ny
while(BFS()){
ans+=DFS(S,2e9);
}
printf("%d\n",ans);
return 0;
}