BZOJ3144: [Hnoi2013]切糕

BZOJ3144: [Hnoi2013]切糕

Description

Input

第一行是三个正整数P,Q,R,表示切糕的长P、 宽Q、高R。

第二行有一个非负整数D,表示光滑性要求。

接下来是R个P行Q列的矩阵,第z个 矩阵的第x行第y列是v(x,y,z) (1≤x≤P, 1≤y≤Q, 1≤z≤R)。 
100%的数据满足P,Q,R≤40,0≤D≤R,且给出的所有的不和谐值不超过1000。

Output

仅包含一个整数,表示在合法基础上最小的总不和谐值。

Sample Input

2 2 2
1
6 1
6 1
2 6
2 6

Sample Output

6

HINT

最佳切面的f为f(1,1)=f(2,1)=2,f(1,2)=f(2,2)=1

题解Here!

乍一看,这题不是计算几何吗?

然后发现,最小值怎么算?

网络流!

把切点看作割边。

新建一个虚拟的层R+1,建立超级源汇点S,T。

先考虑没有光滑限制怎么做。

先由源点S向第1层的每一个点连一条边,再由第R+1层的每一个点向汇点T连一条边,这些边是割不掉的,所以容量都为MAX。

然后对于任何一个1<= i<= P,1<= j<= Q,1<= k<= R,由 ( i , j , k )向 ( i , j , k+1 )连一条流量为 val[ i ][ j ][ k ] 的不和谐值的边。

显然,对于任何一个1<= i<= P,1<= j<= Q,从 ( i , j , 1 ) 到 ( i , j , R+1 ) 的路径上需要且只需要割掉一条边。

再考虑加上光滑限制。

可以发现对于任意一个在同一平面上距离为1的两个点对 ( i , j ),( x , y ),其实只要限制 f( i , j ) - f( x , y ) <= D 就可以了。

因为如果有存在 f( i , j ) - f( x , y ) <= D ,那么一定有 f( x , y ) - f( i , j ) > D ,自然不符合条件。

怎样限制这个条件呢?

可以发现,我们的目标其实就是让 f( i , j ) - f( x , y ) > D 时,S仍然可以到达T。

也就是对于任意一个在同一平面上距离为1的两个点对 ( i , j ),( x , y ) ,对于任何一个D+1 <= k <= R+1,由 ( i , j , k ) 向 ( x , y , k-D ) 连一条流量为MAX的割不掉的边。

最后求最小割即为答案。

最小割==最大流,直接Dinic。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#define MAXN 300010
#define MAXM 50
#define MAX 999999999
using namespace std;
const int fx[4]={1,-1,0,0},fy[4]={0,0,1,-1};
int n,m,p,d,s,t,c=2;
int head[MAXN],deep[MAXN],val[MAXM][MAXM][MAXM];
struct node{
    int next,to,w;
}a[MAXN<<1];
inline int read(){
    int date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    return date*w;
}
inline void add(int u,int v,int w){
    a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++;
    a[c].to=u;a[c].w=0;a[c].next=head[v];head[v]=c++;
}
bool bfs(){
    int u,v;
    queue<int> q;
    for(int i=s;i<=t;i++)deep[i]=0;
    deep[s]=1;
    q.push(s);
    while(!q.empty()){
        u=q.front();
        q.pop();
        for(int i=head[u];i;i=a[i].next){
            v=a[i].to;
            if(a[i].w&&!deep[v]){
                deep[v]=deep[u]+1;
                if(v==t)return true;
                q.push(v);
            }
        }
    }
    return false;
}
int dfs(int x,int limit){
    if(x==t)return limit;
    int v,sum,cost=0;
    for(int i=head[x];i;i=a[i].next){
        v=a[i].to;
        if(a[i].w&&deep[v]==deep[x]+1){
            sum=dfs(v,min(a[i].w,limit-cost));
            if(sum>0){
                a[i].w-=sum;
                a[i^1].w+=sum;
                cost+=sum;
                if(limit==cost)break;
            }
            else deep[v]=-1;
        }
    }
    return cost;
}
int dinic(){
    int ans=0;
    while(bfs())ans+=dfs(s,MAX);
    return ans;
}
void work(){
    printf("%d\n",dinic());
}
void init(){
    int u,v,w;
    n=read();m=read();p=read();d=read();
    s=1;t=n*m*(p+1)+2;
    for(int k=1;k<=p;k++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    val[i][j][k]=read();
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++){
        u=(i-1)*m+j+1;
        add(s,u,MAX);
        for(int k=1;k<=p;k++)add(n*m*(k-1)+u,n*m*k+u,val[i][j][k]);
        add(n*m*p+u,t,MAX);
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    for(int l=0;l<4;l++){
        u=i+fx[l];v=j+fy[l];
        if(u<1||u>n||v<1||v>m)continue;
        for(int k=d+1;k<=p+1;k++)add((n*m*(k-1)+m*(i-1)+j+1),n*m*(k-d-1)+m*(u-1)+v+1,MAX);
    }
}
int main(){
    init();
    work();
    return 0;
}

 

posted @ 2018-08-04 08:08  符拉迪沃斯托克  阅读(202)  评论(0编辑  收藏  举报
Live2D