[BZOJ 3144] 切糕

Link:

BZOJ 3144 传送门

Solution:

发现要把点集分成不连通的两部分,最小割的模型还是很明显的

首先我们将原图转化为$R+1$层,从而将点权化为边权

关键还是在于建图是怎么保证$|h_i-h_j|<=D$这个条件

 

要保证$|h_i-h_j|<=D$这个条件也就意味着选了$i$就不能选$j$,但仍然要保证$i->j$的连通性

 

于是我们由$i+D$向$i$连一条边权为$INF$的边,

这样如果割掉$i,j(j>i+D)$但不选择它们之间的边,就不会影响ST的连通性

只能再割掉$INF$边或其他边,这样两边同时选择明显不会计入答案

同时$i,j(j<=i+D)$就不会出现这样的问题,于是通过设置$INF$边就解决了这个问题

 

   

如上图,如果仅割掉右侧绿边和左侧红边,中间黑色的$INF$边仍会保持ST的连通性

Code:

#include <bits/stdc++.h>

using namespace std;

const int MAXN=50*50*50;
const int INF=2147483647;
int P,Q,R,D,S,T,id[50][50][50],iter[MAXN],level[MAXN],cnt=1,x;
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
struct edge
{
    int to,cap,rev;
};
vector<edge> G[MAXN];

void add_edge(int from,int to,int cap)
{
    G[from].push_back(edge{to,cap,G[to].size()});
    G[to].push_back(edge{from,0,G[from].size()-1});
}

bool bfs()
{
    memset(level,-1,sizeof(level));
    queue<int> que;que.push(S);level[S]=0;
    while(!que.empty())
    {
        int v=que.front();que.pop();
        for(int i=0;i<G[v].size();i++)
        {
            edge &e=G[v][i];
            if(e.cap && level[e.to]==-1)
                level[e.to]=level[v]+1,que.push(e.to);
        }
    }
    return (level[T]>0);
}

int dfs(int v,int f)
{
    if(v==T) return f;
    for(int &i=iter[v];i<G[v].size();i++)
    {
        edge &e=G[v][i];
        if(level[e.to]==level[v]+1 && e.cap)
        {
            int d=dfs(e.to,min(f,e.cap));
            if(d)
            {
                e.cap-=d;G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}

int Dinic()
{
    int ret=0;
    while(bfs())
    {
        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(S,INF))>0) ret+=f;
    }
    return ret;
}

int main()
{
    scanf("%d%d%d%d",&P,&Q,&R,&D);
    S=1;
    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]=++cnt;
    T=++cnt;
    
    for(int i=1;i<=R;i++) for(int j=1;j<=P;j++) for(int k=1;k<=Q;k++)
        scanf("%d",&x),add_edge(id[i][j][k],id[i+1][j][k],x);
        
    for(int i=1;i<=P;i++) for(int j=1;j<=Q;j++)
        add_edge(S,id[1][i][j],INF),add_edge(id[R+1][i][j],T,INF);
    
    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 dir=0;dir<4;dir++)
        {
            int fx=j+dx[dir],fy=k+dy[dir];
            if(!id[i-D][fx][fy]) continue;
            add_edge(id[i][j][k],id[i-D][fx][fy],INF);
        }
    
    printf("%d",Dinic());
    return 0;
}

Review:

 1、认真审题,不要主观带入

这题一开始想成了切割线必须在同一个平面,还是不能读题时自己想当然啊

 

2、最小割模型的新套路

最小割的重点便在于割边计入答案

如果不想让两条边同时计入答案,可以在它们之间加入一条$INF$边,

保证同时选择两边时不会影响原图ST的连通性且$INF$边不会影响结果

使得同时选择两边时一定不符合最小的条件,从而不会计入答案

 

3、点权变边权

可以通过点变边,边变点的方式完成这样的转换

 

posted @ 2018-06-02 22:32  NewErA  阅读(202)  评论(0编辑  收藏  举报