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;
}