zoukankan      html  css  js  c++  java
  • [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、点权变边权

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

  • 相关阅读:
    【BZOJ】1620: [Usaco2008 Nov]Time Management 时间管理(贪心)
    【BZOJ】1651: [Usaco2006 Feb]Stall Reservations 专用牛棚(线段树/前缀和 + 差分)
    【BZOJ】1628 && 1683: [Usaco2007 Demo]City skyline 城市地平线(单调栈)
    【BZOJ】1624: [Usaco2008 Open] Clear And Present Danger 寻宝之路(floyd)
    【BZOJ】1622: [Usaco2008 Open]Word Power 名字的能量(dp/-模拟)
    【BZOJ】1634: [Usaco2007 Jan]Protecting the Flowers 护花(贪心)
    【BZOJ】1690: [Usaco2007 Dec]奶牛的旅行(分数规划+spfa)
    【BZOJ】1660: [Usaco2006 Nov]Bad Hair Day 乱发节(单调栈)
    【BZOJ】1642: [Usaco2007 Nov]Milking Time 挤奶时间(dp)
    【BZOJ】1629: [Usaco2007 Demo]Cow Acrobats(贪心+排序)
  • 原文地址:https://www.cnblogs.com/newera/p/9127312.html
Copyright © 2011-2022 走看看