题目传送门:https://www.luogu.org/problemnew/show/P3227
题意:
有一个p*q*r的切糕,现在要将它切开,规定相邻纵轴的切割点的距离不能超过d。现在每个点(x,y,z)都有一个价值,现在要找到一种切切糕的方式使得价值最小。
PS:切糕是一种食物。
思路:
师兄太强了。
最小割。
构图:
1.源点向第一层建边,流量为无限大(表示不可以被割掉);
2.第r+1层向汇点建边,流量为无限大(表示不可以被割掉);
3.每一个点(x,y,z)都向它下一层的点(x+1,y,z)连一条为它的价值的边;
4.每个点(x,y,z)向它前面d层的点(x-d,y,z)连一条流量为INF的边。
第4点最不好懂。
(以下摘自Zarxdy34,讲得非常好)
下面是源,上面是汇。如图所示,如果我把图中的红边割掉,那么图中的蓝边也就没有用了。如果此时把绿边割掉,那么还是存在一条从源到汇的路径;如果割绿色的边上面的边,那么源到汇就没有路径可达了。
仔细理解一下,也就是在左边选了一条边以后,右边的高度小于当前边的边就不能选了。如果把右上向左下连的边也连起来,那么高度就完全限制住了。
代码:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define INF 2147483647
using namespace std;
queue<int> f;
const int fx[4]={0,0,1,-1};
const int fy[4]={1,-1,0,0};
struct node{int x,y,z,next;} a[1000000];
int id[50][50][50],last[100000];
int p,q,r,d,len=-1,ans=0,st,ed;
void ins(int x,int y,int z)
{
a[++len].x=x;a[len].y=y;a[len].z=z;a[len].next=last[x];last[x]=len;
}
int h[100000];
bool bfs()
{
memset(h,0,sizeof(h));
h[st]=1;
f.push(st);
while(!f.empty())
{
int x=f.front();
for(int i=last[x];i>=0;i=a[i].next)
{
int y=a[i].y;
if(a[i].z>0&&h[y]==0)
{
h[y]=h[x]+1;
f.push(y);
}
}
f.pop();
}
if(h[ed]) return true; else return false;
}
int dfs(int x,int f)
{
int s=0,t;
if(x==ed) return f;
for(int i=last[x];i>=0;i=a[i].next)
{
int y=a[i].y;
if(a[i].z>0&&h[y]==h[x]+1&&f>s)
{
s+=(t=(dfs(y,min(f-s,a[i].z))));
a[i].z-=t;
a[i^1].z+=t;
}
}
if(!s) h[x]=0;
return s;
}
int dinic()
{
int sum=0;
while(bfs())
sum+=dfs(st,INF);
return sum;
}
int main()
{
int x;
scanf("%d %d %d",&p,&q,&r);
scanf("%d",&d);
st=0,ed=(r+1)*p*q+1;
memset(last,-1,sizeof(last));
int u=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]=++u;
for(int j=1;j<=p;j++)
for(int k=1;k<=q;k++)
{
ins(st,id[1][j][k],INF),ins(id[1][j][k],st,0);
ins(id[1+r][j][k],ed,INF),ins(ed,id[1+r][j][k],0);
}
for(int i=1;i<=r;i++)
for(int j=1;j<=p;j++)
for(int k=1;k<=q;k++)
{
scanf("%d",&x);
ins(id[i][j][k],id[i+1][j][k],x),ins(id[i+1][j][k],id[i][j][k],0);
}
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 l=0;l<=3;l++)
{
int t1=j+fx[l],t2=k+fy[l];
if(t1<0||t1>p||t2<0||t2>q) continue;
ins(id[i][j][k],id[i-d][t1][t2],INF),ins(id[i-d][t1][t2],id[i][j][k],0);
}
printf("%d",dinic());
}