【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

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

题解:考虑限制1:我们只需要将(x,y)相同的从上到下依次连起来即可,上面的点向下面的点连下面点权值的边。

           考虑限制2:我们必须保证选不合法的点的集合的时候图仍然是联通的。

                               所以对于每个点,我们只需要从(x,y,z)向(x,y,z+d)连一条inf的边即可。

           这样无法统计第一层点的权值,所以我们对于每个(x,y)建一个虚拟节点,从源点向它连inf的边,从它向第一层(x,y)连那个点权值的边即可。

最小割即是答案。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 70000
#define M 400000
#define INF 2100000000
using namespace std;
int point[N],next[M<<1],cnt(1),p,q,r,dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
int cur[N],dis[N],pre[N],gap[N],a[50][50][50],d,T;
struct use{int st,en,v;}e[M<<1];
bool f;
void add(int x,int y,int v){
  next[++cnt]=point[x];point[x]=cnt;e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
  next[++cnt]=point[y];point[y]=cnt;e[cnt].st=y;e[cnt].en=x;e[cnt].v=0;
}
int isap(int ss,int tt){
  int mn,u(ss),i,ans(0);gap[0]=T;
  for (int i=1;i<=T;i++) cur[i]=point[i];
  while (dis[ss]<=T){
    f=false;
    for (i=cur[u];i;i=next[i])
     if (e[i].v&&dis[e[i].en]+1==dis[u]){f=true;cur[u]=i;break;}
    if (f){
      pre[u=e[i].en]=i;
      if (u==tt){
        mn=INF;
        for (i=tt;i!=ss;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
        ans+=mn;
        for (i=tt;i!=ss;i=e[pre[i]].st) e[pre[i]].v-=mn,e[pre[i]^1].v+=mn;
        u=ss;
	  }
    }
    else{
      gap[dis[u]]--;if (!gap[dis[u]]) return ans;
      for (mn=T,i=point[u];i;i=next[i]) 
       if (e[i].v) mn=min(mn,dis[e[i].en]);
      gap[dis[u]=mn+1]++;cur[u]=point[u];
      if (u!=ss) u=e[pre[u]].st;
    }
  }
 return ans;
}
int F(int x,int y){return (x-1)*q+y;}
int cal(int x,int y,int z){return (x-1)*p*q+F(y,z);}
int main(){
  scanf("%d%d%d",&p,&q,&r);scanf("%d",&d);
  int t=p*q*r;T=p*q*r+2+p*q;
  for (int i=1;i<=r;i++)
   for (int j=1;j<=p;j++)
    for (int k=1;k<=q;k++)
     scanf("%d",&a[i][j][k]);
  for (int i=1;i<=r;i++)
   for (int j=1;j<=p;j++)
    for (int k=1;k<=q;k++){
      if (i==1) add(cal(i,j,k)+1,T,INF);
      if (i==r) add(t+F(j,k)+1,cal(i,j,k)+1,a[i][j][k]);
      else add(cal(i+1,j,k)+1,cal(i,j,k)+1,a[i][j][k]); 
	}
  for (int i=1;i<=p;i++)
   for (int j=1;j<=q;j++)
    add(1,F(i,j)+1+t,INF);    	
  for (int i=1;i<=r;i++)
   for (int j=1;j<=p;j++)
    for (int k=1;k<=q;k++)
     for (int l=0;l<4;l++){
       int xx=j+dx[l],yy=k+dy[l],zz=i+d;
       if (xx<1||xx>p||yy<1||yy>q||zz<1||zz>r) continue;
       add(cal(i,j,k)+1,cal(zz,xx,yy)+1,INF);
     }
  cout<<isap(1,T)<<endl;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值