HNOI2013 切糕

题目链接:戳我

最小割的一种常见模型。
题目可以转化为:给定一个立方体,它被分成pqr个格子,每个格子中都有一个数字,相邻地选择数字,每一行每一列都要选择一个数字。要求这些数字和最小。

建图就是建r+1层,然后这些边的权值是对应位置的数字权值。从每一个点(第z层)向它旁边四个纵轴连一条指向第z-d层的有向边(这样可以限制旁边纵轴上选择的点的层数>=z-d,但是我们想要的是在>=z-d的同时还<=z+d。这个不用担心,因为它旁边的纵轴也会有和它同样的操作连上去有向边,在层数>z+d的时候,它通过指向>z的点的有向边依然可以流向汇点——所以说这样就限制住了层数)。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define S 0
#define T cnt+1
#define MAXN 100010
#define INF 0x3f3f3f3f
using namespace std;
int P,Q,R,t=1,d,cnt;
int head[MAXN],dep[MAXN],cur[MAXN],sum[50][50][50],id[50][50][50];
struct Edge{int nxt,to,dis;}edge[MAXN<<1];

inline void add(int from,int to,int dis)
{
    edge[++t].nxt=head[from],edge[t].to=to,edge[t].dis=dis,head[from]=t;
    edge[++t].nxt=head[to],edge[t].to=from,edge[t].dis=0;head[to]=t;
}

inline bool bfs()
{
    queue<int>q;
    memset(dep,0x3f,sizeof(dep));
    memcpy(cur,head,sizeof(head));
    q.push(S);dep[S]=0;
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(dep[v]==0x3f3f3f3f&&edge[i].dis)
                dep[v]=dep[u]+1,q.push(v);
        }
    }
    if(dep[T]==0x3f3f3f3f) return false;
    return true;
}

inline int dfs(int x,int f)
{
    if(!f||x==T) return f;
    int w,used=0;
    for(int i=cur[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        cur[x]=i;
        if(dep[v]==dep[x]+1&&(w=dfs(v,min(f,edge[i].dis))))
        {
            edge[i].dis-=w,edge[i^1].dis+=w;
            used+=w,f-=w;
            if(!f) break;
        }
    }
    return used;
}

inline int dinic()
{
    int cur_ans=0;
    while(bfs()) cur_ans+=dfs(S,INF);
    return cur_ans;
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce1.in","r",stdin);
    #endif
    scanf("%d%d%d",&P,&Q,&R);
    for(int z=1;z<=R+1;z++)
        for(int x=1;x<=P;x++)
            for(int y=1;y<=Q;y++)
                id[x][y][z]=++cnt;
    scanf("%d",&d);
    for(int z=1;z<=R;z++)
        for(int x=1;x<=P;x++)
            for(int y=1;y<=Q;y++)
                scanf("%d",&sum[x][y][z]);
    for(int z=1;z<=R+1;z++)
    {
        for(int x=1;x<=P;x++)
        {
            for(int y=1;y<=Q;y++)
            {
                if(z==1) add(S,id[x][y][z],INF);//printf("[%d,%d] %d\n",S,id[x][y][z],INF);
                if(z==R+1) add(id[x][y][z],T,INF);//printf("[%d,%d] %d\n",id[x][y][z],T,INF);
                else add(id[x][y][z],id[x][y][z+1],sum[x][y][z]);//printf("[%d,%d] %d\n",id[x][y][z],id[x][y][z+1],sum[x][y][z]);
                if(z>d) 
                {
                    if(y+1<=Q) add(id[x][y][z],id[x][y+1][z-d],INF);//printf("[%d,%d] %d\n",id[x][y][z],id[x][y+1][z-d],INF);
                    if(y-1>=1) add(id[x][y][z],id[x][y-1][z-d],INF);//printf("[%d,%d] %d\n",id[x][y][z],id[x][y-1][z-d],INF);
                    if(x+1<=P) add(id[x][y][z],id[x+1][y][z-d],INF);//printf("[%d,%d] %d\n",id[x][y][z],id[x+1][y][z-d],INF);
                    if(x-1>=1) add(id[x][y][z],id[x-1][y][z-d],INF);//printf("[%d,%d] %d\n",id[x][y][z],id[x-1][y][z-d],INF);
                }
            }
        }
    }
    printf("%d\n",dinic());
    return 0;
}
posted @ 2019-02-12 12:12  风浔凌  阅读(194)  评论(0编辑  收藏  举报