[bzoj3144]【HNOI2013】切糕

来源:互联网 发布:win10使用linux子系统 编辑:程序博客网 时间:2024/04/26 06:24

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 64505using 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);}
0 0
原创粉丝点击