题目描述
题解
很典型的最小割啊
切糕的每一小块就是一个点,然后再在切糕的最上面一层建一层P*Q个虚拟的点
s向最上面一层点连边,最下面一层点向t连边
对于第一个限制,每一个点向它下面的那个点连边,容量为下面那个点的权值
对于第二个限制,每一个点向它上面第d个点相邻的四个点连边,容量为inf,这样就保证了如果割的不是距离d以内的点,源点和汇点还是连通的,一定不是一种最小割的方案
然后做最小割就行了
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define N 70005
#define E 700005
#define inf 2000000000
int P,Q,R,d,s,t,maxflow;
int val[45][45][45];
int tot,point[N],nxt[E],v[E],remain[E];
int deep[N],last[N],num[N],cur[N];
queue <int> q;
void addedge(int x,int y,int cap)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}
void bfs(int t)
{
for (int i=1;i<=t;++i) deep[i]=t;
deep[t]=0;
for (int i=1;i<=t;++i) cur[i]=point[i];
while (!q.empty()) q.pop();
q.push(t);
while (!q.empty())
{
int now=q.front();q.pop();
for (int i=point[now];i!=-1;i=nxt[i])
if (deep[v[i]]==t&&remain[i^1])
{
deep[v[i]]=deep[now]+1;
q.push(v[i]);
}
}
}
int addflow(int s,int t)
{
int now=t,ans=inf;
while (now!=s)
{
ans=min(ans,remain[last[now]]);
now=v[last[now]^1];
}
now=t;
while (now!=s)
{
remain[last[now]]-=ans;
remain[last[now]^1]+=ans;
now=v[last[now]^1];
}
return ans;
}
void isap(int s,int t)
{
bfs(t);
for (int i=1;i<=t;++i) ++num[deep[i]];
int now=s;
while (deep[s]<t)
{
if (now==t)
{
maxflow+=addflow(s,t);
now=s;
}
bool has_find=false;
for (int i=cur[now];i!=-1;i=nxt[i])
if (deep[v[i]]+1==deep[now]&&remain[i])
{
has_find=true;
cur[now]=i;
last[v[i]]=i;
now=v[i];
break;
}
if (!has_find)
{
int minn=t-1;
for (int i=point[now];i!=-1;i=nxt[i])
if (remain[i]) minn=min(minn,deep[v[i]]);
if (!(--num[deep[now]])) break;
++num[deep[now]=minn+1];
cur[now]=point[now];
if (now!=s) now=v[last[now]^1];
}
}
}
int main()
{
tot=-1;memset(point,-1,sizeof(point));
scanf("%d%d%d",&P,&Q,&R);
scanf("%d",&d);
s=P*Q*(R+1)+1,t=s+1;
for (int k=1;k<=R;++k)
for (int i=1;i<=P;++i)
for (int j=1;j<=Q;++j)
scanf("%d",&val[i][j][k]);
for (int k=R+1;k>1;--k)
for (int i=1;i<=P;++i)
for (int j=1;j<=Q;++j)
{
int up=(k-1)*P*Q+(i-1)*Q+j;
int down=up-P*Q;
addedge(up,down,val[i][j][k-1]);
}
for (int k=1;k<=R;++k)
{
if (k+d>R) break;
for (int i=1;i<=P;++i)
for (int j=1;j<=Q;++j)
{
int down=(k-1)*P*Q+(i-1)*Q+j;
int up=down+d*P*Q;
if (i!=1) addedge(down,up-Q,inf);
if (j!=1) addedge(down,up-1,inf);
if (i!=P) addedge(down,up+Q,inf);
if (j!=Q) addedge(down,up+1,inf);
}
}
for (int i=1;i<=P;++i)
for (int j=1;j<=Q;++j)
addedge(s,(i-1)*Q+j+P*Q*R,inf),addedge((i-1)*Q+j,t,inf);
isap(s,t);
printf("%d\n",maxflow);
}