• [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、点权变边权

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

  • 相关阅读:
    Leetcode Plus One
    Leetcode Swap Nodes in Pairs
    Leetcode Remove Nth Node From End of List
    leetcode Remove Duplicates from Sorted Array
    leetcode Remove Element
    leetcode Container With Most Water
    leetcode String to Integer (atoi)
    leetcode Palindrome Number
    leetcode Roman to Integer
    leetcode ZigZag Conversion
  • 原文地址:https://www.cnblogs.com/newera/p/9127312.html
Copyright © 2020-2023  润新知