[bzoj3144]【HNOI2013】切糕

Description

表示语文不好,看了好久才看懂题。
化简之后的题意大概是这样的:给出一个P*Q的网格,在(x,y)放一个数字z(1<=z<=R)的代价是v(x,y,z),并且四相邻中格子的数的绝对值差不能大于D。
求最小代价。
P,Q,R≤40,0≤D≤R

Solution

如果你看懂了原题,那么应该可以很容易的想到最小割。
因为化简之后题目反而混乱了。
也就是说,我们要把这块切糕分成两个集合。
一个简单的想法是每个点向它的下一层的点连一条边权为它的权值的边。
第一层的点连向源点。
最后一层的点连汇点连正无穷。
如何保证限制?
我们只需要每个点向它相邻的层数差D的点连一条正无穷的边。
画出图来就知道正确性显然了。。。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 64505
using namespace std;
const int inf=0x7fffffff;
int dis[N],d[N],D,S,T,l,all,p,q,r,x,ans;
int last[N],next[N*12],t[N*12],f[N*12],g[4][2]={0,1,1,0,0,-1,-1,0};
int get(int x,int y,int z) {
    return (x-1)*p*q+(y-1)*q+z;
}
void add(int x,int y,int z) {
    t[++l]=y;f[l]=z;next[l]=last[x];last[x]=l;
    t[++l]=x;f[l]=0;next[l]=last[y];last[y]=l;
}
bool bfs() {
    memset(dis,0,sizeof(dis));
    int i=0,j=1;d[1]=S;dis[S]=1;
    while (i<j) rep(k,d[++i]) 
        if (!dis[t[k]]&&f[k]) {
            dis[t[k]]=dis[d[i]]+1;
            d[++j]=t[k];
        } 
    return dis[T];
}
int dinic(int x,int y) {
    if (x==T) return y;
    int now=0;
    rep(i,x) if (f[i]&&dis[t[i]]==dis[x]+1) {
        int k=dinic(t[i],min(f[i],y));
        f[i]-=k;f[i^1]+=k;y-=k;now+=k;
        if (!y) break;
    }
    if (!now) dis[x]=-1;
    return now;
}
int main() {
    scanf("%d%d%d%d",&p,&q,&r,&D);all=p*q*r;S=0;T=all+1;l=1;
    fo(i,1,r) fo(j,1,p) fo(k,1,q) {
        scanf("%d",&x);
        if (i==1) add(S,get(i,j,k),x);
        else add(get(i-1,j,k),get(i,j,k),x);
        if (i>D) 
            fo(s,0,3) {
                int x=j+g[s][0],y=k+g[s][1];
                if (x<1||x>p||y<1||y>q) continue;
                add(get(i,j,k),get(i-D,x,y),inf);
            }
        if (i==r) add(get(i,j,k),T,inf);
    } 
    while (bfs()) ans+=dinic(S,inf);
    printf("%d",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值