HNOI2013 切糕
题目链接:戳我
最小割的一种常见模型。
题目可以转化为:给定一个立方体,它被分成pqr个格子,每个格子中都有一个数字,相邻地选择数字,每一行每一列都要选择一个数字。要求这些数字和最小。
建图就是建r+1层,然后这些边的权值是对应位置的数字权值。从每一个点(第z层)向它旁边四个纵轴连一条指向第z-d层的有向边(这样可以限制旁边纵轴上选择的点的层数>=z-d,但是我们想要的是在>=z-d的同时还<=z+d。这个不用担心,因为它旁边的纵轴也会有和它同样的操作连上去有向边,在层数>z+d的时候,它通过指向>z的点的有向边依然可以流向汇点——所以说这样就限制住了层数)。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define S 0
#define T cnt+1
#define MAXN 100010
#define INF 0x3f3f3f3f
using namespace std;
int P,Q,R,t=1,d,cnt;
int head[MAXN],dep[MAXN],cur[MAXN],sum[50][50][50],id[50][50][50];
struct Edge{int nxt,to,dis;}edge[MAXN<<1];
inline void add(int from,int to,int dis)
{
edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0;head[to]=t;
}
inline bool bfs()
{
queue<int>q;
memset(dep,0x3f,sizeof(dep));
memcpy(cur,head,sizeof(head));
q.push(S);dep[S]=0;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].to;
if(dep[v]==0x3f3f3f3f&&edge[i].dis)
dep[v]=dep[u]+1,q.push(v);
}
}
if(dep[T]==0x3f3f3f3f) return false;
return true;
}
inline int dfs(int x,int f)
{
if(!f||x==T) return f;
int w,used=0;
for(int i=cur[x];i;i=edge[i].nxt)
{
int v=edge[i].to;
cur[x]=i;
if(dep[v]==dep[x]+1&&(w=dfs(v,min(f,edge[i].dis))))
{
edge[i].dis-=w,edge[i^1].dis+=w;
used+=w,f-=w;
if(!f) break;
}
}
return used;
}
inline int dinic()
{
int cur_ans=0;
while(bfs()) cur_ans+=dfs(S,INF);
return cur_ans;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce1.in","r",stdin);
#endif
scanf("%d%d%d",&P,&Q,&R);
for(int z=1;z<=R+1;z++)
for(int x=1;x<=P;x++)
for(int y=1;y<=Q;y++)
id[x][y][z]=++cnt;
scanf("%d",&d);
for(int z=1;z<=R;z++)
for(int x=1;x<=P;x++)
for(int y=1;y<=Q;y++)
scanf("%d",&sum[x][y][z]);
for(int z=1;z<=R+1;z++)
{
for(int x=1;x<=P;x++)
{
for(int y=1;y<=Q;y++)
{
if(z==1) add(S,id[x][y][z],INF);//printf("[%d,%d] %d\n",S,id[x][y][z],INF);
if(z==R+1) add(id[x][y][z],T,INF);//printf("[%d,%d] %d\n",id[x][y][z],T,INF);
else add(id[x][y][z],id[x][y][z+1],sum[x][y][z]);//printf("[%d,%d] %d\n",id[x][y][z],id[x][y][z+1],sum[x][y][z]);
if(z>d)
{
if(y+1<=Q) add(id[x][y][z],id[x][y+1][z-d],INF);//printf("[%d,%d] %d\n",id[x][y][z],id[x][y+1][z-d],INF);
if(y-1>=1) add(id[x][y][z],id[x][y-1][z-d],INF);//printf("[%d,%d] %d\n",id[x][y][z],id[x][y-1][z-d],INF);
if(x+1<=P) add(id[x][y][z],id[x+1][y][z-d],INF);//printf("[%d,%d] %d\n",id[x][y][z],id[x+1][y][z-d],INF);
if(x-1>=1) add(id[x][y][z],id[x-1][y][z-d],INF);//printf("[%d,%d] %d\n",id[x][y][z],id[x-1][y][z-d],INF);
}
}
}
}
printf("%d\n",dinic());
return 0;
}