Luogu P3227 [HNOI2013]切糕

%%ZZKdalao上课讲的题目,才知道网络流的这种玄学建模

我们先想一想,如果没有D的限制,那么想当于再每一根纵轴上选一个权值最小的点再加起来

我们对应在网络流上就是每一根纵轴上的点向它下方的点用权值当边值进行连边,然后要割掉一些边,代价最小就是求最小割

然后我们考虑限制,就是如果割了某一根数轴上高度为x的点,那么所有与它相邻的纵轴都只能割高度为[x-d,x+d]的点

这个时候我们就要知道一个常用技巧:在求最小割时,我们可以把那些无法割去的边边权设为INF

因此我们在建边时,由纵轴上一度为x的点高向与它相邻的纵轴上高度为x-d的点连边,边权为INF

为什么呢,我们结合一个图来看一下:

其中红色的边表示边权为INF,无法割去

当我们选择割掉5-7的这条边时,会发现2-4这条边无法割去。因为就算割去了也可以从5-4这条边过去。这就达到了我们的目的

因此我们这样建边之后跑最大流即可

CODE

#include<cstdio>
#include<cstring>
using namespace std;
const int N=45,INF=1e9,fx[4]={0,1,0,-1},fy[4]={1,0,-1,0};
struct edge
{
    int to,next,c;
}e[N*N*N*20];
int v[N][N][N],head[N*N*N],dep[N*N*N],Q[N*N*N],p,q,r,d,s,t,cnt=-1;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch=tc();
    while (ch<'0'||ch>'9') ch=tc();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline int get_num(int x,int y,int z)
{
    return x*p*q+y*q+z;
}
inline void add(int x,int y,int z)
{
    e[++cnt].to=y; e[cnt].c=z; e[cnt].next=head[x]; head[x]=cnt;
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline bool BFS(void)
{
    memset(dep,0,sizeof(dep));
    dep[s]=1; Q[1]=s;
    int H=0,T=1;
    while (H<T)
    {
        int now=Q[++H];
        for (register int i=head[now];i!=-1;i=e[i].next)
        if (!dep[e[i].to]&&e[i].c)
        {
            dep[e[i].to]=dep[now]+1;
            Q[++T]=e[i].to;
        }
    }
    return dep[t];
}
inline int DFS(int now,int dist)
{
    if (now==t) return dist;
    int res=0;
    for (register int i=head[now];i!=-1&&dist;i=e[i].next)
    if (dep[e[i].to]==dep[now]+1&&e[i].c)
    {
        int dis=DFS(e[i].to,min(dist,e[i].c));
        dist-=dis; res+=dis;
        e[i].c-=dis; e[i^1].c+=dis;
    }
    if (!res) dep[now]=0;
    return res;
}
inline int Dinic(void)
{
    int res=0;
    while (BFS()) res+=DFS(s,INF);
    return res;
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i,j,k;
    memset(e,-1,sizeof(e));
    memset(head,-1,sizeof(head));
    read(p); read(q); read(r); read(d); s=0; t=get_num(r,p,q)+1;
    for (i=1;i<=r;++i)
    for (j=1;j<=p;++j)
    for (k=1;k<=q;++k)
    read(v[i][j][k]);
    for (i=1;i<=p;++i)
    for (j=1;j<=q;++j)
    add(s,get_num(0,i,j),INF),add(get_num(0,i,j),s,0),add(get_num(r,i,j),t,INF),add(t,get_num(r,i,j),0);
    for (i=1;i<=r;++i)
    for (j=1;j<=p;++j)
    for (k=1;k<=q;++k)
    add(get_num(i-1,j,k),get_num(i,j,k),v[i][j][k]),add(get_num(i,j,k),get_num(i-1,j,k),0);
    for (i=d;i<=r;++i)
    for (j=1;j<=p;++j)
    for (k=1;k<=q;++k)
    for (register int kind=0;kind<4;++kind)
    {
        int x=j+fx[kind],y=k+fy[kind];
        if (x>0&&x<=p&&y>0&&y<=q)
        add(get_num(i,j,k),get_num(i-d,x,y),INF),add(get_num(i-d,x,y),get_num(i,j,k),0);
    }
    printf("%d",Dinic());
    return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/8893421.html