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
1
6 1
6 1
2 6
2 6
Sample Output
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; }