【HNOI2013】切糕

Description

经过千辛万苦小A 得到了一块切糕,切糕的形状是长方体,小A 打算拦腰将切糕切成两半分给小B。出于美观考虑,小A 希望切面能尽量光滑且和谐。于是她找到你,希望你能帮她找出最好的切割方案。

出于简便考虑,我们将切糕视作一个长P、宽Q、高R 的长方体点阵。我们将位于第z层中第x 行、第y 列上(1≤x≤P, 1≤y≤Q, 1≤z≤R)的点称为(x,y,z),它有一个非负的不和谐值v(x,y,z)。一个合法的切面满足以下两个条件:

  1. 与每个纵轴(一共有P*Q 个纵轴)有且仅有一个交点。即切面是一个函数f(x,y),对于所有1≤x≤P, 1≤y≤Q,我们需指定一个切割点f(x,y),且1≤f(x,y)≤R。

  2. 切面需要满足一定的光滑性要求,即相邻纵轴上的切割点不能相距太远。对于所有的1≤x,x’≤P 和1≤y,y’ ≤Q,若|x-x’|+|y-y’|=1,则|f(x,y)-f(x’,y’)| ≤D,其中D 是给定的一个非负整数。

可能有许多切面f 满足上面的条件,小A 希望找出总的切割点上的不和谐值最小的那个,即这里写图片描述最小。

Solution

题意太难看懂呢(自愧语文不好)
首先有一个差的限制,这是一个很经典的连边操作。
首先我们看一下要求最小值,那么最小割的集合就是答案。
显然每个柱子(x,y)只能选一个,那么每个(x,y)中的每个数从头到尾依次连边是可以满足割一条那么这个柱子就可以断掉。那么每条边的边权就代表这个柱子第i个的值。多开一个0节点,S向这个0节点连inf(割不掉),最后一个点向T连inf。
然后我们考虑高度绝对值满足≤D的操作。
假如现在有两条链,边的方向都是指向末尾的,然后第i个点,如果要满足在a链割掉了第i个,然后在b链必须割掉序号≥i-d的,那么就必须从a链的i向b链的i-d连一条inf的有向边(关系割不掉)才能实现:如果割了i,但是i-d到末尾的边都没有被割掉,那么S和T还会连通。
这题是要满足绝对值≤D,那么还需要把i+d连向i。所以每个点i都要向相邻的柱子连到i-d,在i+d的时候也会连到i。
然后做一遍最小割就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
const int maxn=1e6+7,inf=0x7fffffff;
int i,j,k,l,t,n,m,ans,r,p,x,y;
int first[maxn],next[maxn],last[maxn],chang[maxn],fan[maxn],num;
int S,T,data[maxn],d[maxn/10];
int fang[4][2]={1,0,0,1,-1,0,0,-1};
int de(int x,int y,int z){return ((x-1)*m+y-1)*(r+1)+z+1;}
void add(int x,int y,int z){
    last[++num]=y,next[num]=first[x],first[x]=num,chang[num]=z,fan[num]=num+1;
    last[++num]=x,next[num]=first[y],first[y]=num,chang[num]=0,fan[num]=num-1;
}
int dinic(int x,int y){
    if(x==T)return y;
    int i,j,p=0;
    rep(i,x){
        if(chang[i]&&d[last[i]]==d[x]+1){
            j=dinic(last[i],min(y,chang[i]));
            if(j){
                chang[i]-=j,chang[fan[i]]+=j;
                p+=j,y-=j;if(!y)break;
            }
        }
    }
    if(!p)d[x]=-1;
    return p;
}
bool bfs(){
    int head=0,tail=1,i,now;
    memset(d,0,sizeof(d));d[data[1]=S]=1;
    while(headif(chang[i]&&!d[last[i]]){
                data[++tail]=last[i];
                d[last[i]]=d[now]+1;
            }
        }
    }
    return d[T]!=0;
}
int main(){
//  freopen("fan.in","r",stdin);
    scanf("%d%d%d%d",&n,&m,&r,&p);
    S=0,T=n*m*(r+1)+1;
    fo(i,1,n)fo(j,1,m)add(S,de(i,j,0),inf),add(de(i,j,r),T,inf);
    fo(k,1,r){
        fo(i,1,n){
            fo(j,1,m){
                scanf("%d",&t);
                add(de(i,j,k-1),de(i,j,k),t);
                fo(l,0,3){
                    x=fang[l][0]+i,y=fang[l][1]+j;
                    if(x<1||x>n||y<1||y>m)continue;
                    add(de(i,j,k-1),de(x,y,max(0,k-p-1)),inf);
                }
            }
        }
    }
    while(bfs())ans+=dinic(S,0x7fffffff);
    printf("%d",ans);
}

你可能感兴趣的:(网络流,省选)