[BZOJ3144]切糕

题目描述

  • 哎,这又是网络流?自闭了。
  • 先考虑只有一个限制条件的情况,求最小收益,转化到求最小割上面。
    在这里插入图片描述
  • 这张图相当于我们把题中的n*m条链抽出来两条,考虑如何通过巧妙地建图,实现对条件的限制。
  • 假如我们有这样一个限制:如果选择了2号点,那么只能选择8~10号点。选择一个点,在图中对应割掉一条边。最小收益我们已经转化为求最小割,考虑增加什么样的边,可以使新图跑最小割的过程中同时满足限制。如果,从2到8号点连一条容量为inf的边,那么假如割掉了2—>3这条边,那么一定会从蓝色的边中割掉一条。这样,通过一条边,我们达到了对后缀的限制。如果是一个可行区间,再从下面的链向上连一条边不就好了?
  • 因此,我们己经会了两条链的情况。本题同样如此,只不过是n*m条链。每条链都这样处理,一条链上相邻点连边的容量为点权,s到第0层的点连容量inf的边,最后一层向t连容量为inf的边。跑最小割即可。
#include<bits/stdc++.h>
#define ll long long
#define rint register int 
using namespace std;
const int N=41*41*41+10;
const int M=3e5+10;
const int inf=1e9;
const int dx[4]={0,0,1,-1};
const int dy[4]={1,-1,0,0};
int P,Q,R,D,s,t,ver[M<<1],Next[M<<1],lin[N],edge[M<<1],d[N],v[41][41][41],flow,maxflow;
int cal(int i,int j,int k){return i*P*Q+j*P+k;}
void add(int x,int y,int z){
    ver[++tot]=y;Next[tot]=lin[x];lin[x]=tot;edge[tot]=z;
    ver[++tot]=x;Next[tot]=lin[y];lin[y]=tot;edge[tot]=0;
}
void init(){
    scanf("%d%d%d%d",&P,&Q,&R,&D);s=N-2,t=N-1;
    for(int i=1;i<=R;++i) for(int j=1;j<=P;++j) for(int k=1;k<=Q;++k) scanf("%d",&v[i][j][k]);
    for(int i=1;i<=P;++i) for(int j=1;j<=Q;++j) add(s,cal(0,i,j),inf),add(cal(R,i,j),t,inf);
    for(int i=1;i<=R;++i) for(int j=1;j<=P;++j) for(int k=1;k<=Q;++k) add(cal(i-1,j,k),cal(i,j,k),v[i][j][k]);
    for(int i=D;i<=R;++i) for(int j=1;j<=P;++j) for(int k=1;k<=Q;++k){
        for(int t=0;t<4;++t){
            int tx=j+dx[t],ty=k+dy[t];
            if(tx<1||tx>P||ty<1||ty>Q) continue;
            add(cal(i,j,k),cal(i-D,tx,ty),inf);
        }
    } 
}
bool bfs(){
    memset(d,0,sizeof(d));
    queue<int>q;
    d[s]=1,q.push(s);
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=lin[x];i;i=Next[i]){
            int y=ver[i];
            if(!d[y]&&edge[i]){
                d[y]=d[x]+1;q.push(y);
                if(y==t) return 1;
            }
        }
    }
}
int dinic(int x,int flow){
    int rest=flow;
    for(int i=lin[x];i&&rest;i=Next[i]){
        int y=ver[i];
        if(edge[i]&&d[y]==d[x]+1){
            int k=dinic(y,min(edge[i],rest));
            if(!k) d[y]=0;
            rest-=k;edge[i]-=k;edge[i^1]+=k;
            if(!rest) return flow-rest;
        }
    }
    return flow-rest;
}
void work(){
    while(bfs()){
        while(flow=dinic(s,inf)) maxflow+=flow;
    }
    cout<<maxflow<<endl;
}
int main(){
    init();
    work();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kgxw0430/p/10542592.html
今日推荐