[HNOI2013]切糕
题目描述
经过千辛万苦小 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)。一个合法的切面满足以下两个条件:
-
与每个纵轴(一共有 P*Q 个纵轴)有且仅有一个交点。即切面是一个函数 f(x,y),对于所有 1≤x≤P, 1≤y≤Q,我们需指定一个切割点 f(x,y),且 1≤f(x,y)≤R。
- 切面需要满足一定的光滑性要求,即相邻纵轴上的切割点不能相距太远。对于所有的 1≤x,x’≤P 和 1≤y,y’≤Q,若|x-x’|+|y-y’|=1,则|f(x,y)-f(x’,y’)| ≤D,其中 D 是给定的一个非负整数。 可能有许多切面f 满足上面的条件,小A 希望找出总的切割点上的不和谐值最小的那个。
输入输出格式
输入格式:
第一行是三个正整数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。
输出格式:
仅包含一个整数,表示在合法基础上最小的总不和谐值。
输入输出样例
输入样例#1:
2 2 2 1 6 1 6 1 2 6 2 6
输出样例#1:
6
将模型简化一下,变成对于两个相邻点(x1,y1)和(x2,y2)的最小和谐值,如图
由于答案是最小,所以考虑最小割
因为一个点只选一层和谐值,所以考虑每相邻楼层建边,每个(x,y)形成一个长为r的路径
此处为方便处理,先再度入时给节点标号,为nu[i][j][k](num重名)
E=(nu[i][j][k],nu[i][j][k+1],w[i][j][k])
接下来考虑怎么使相邻高度差不大于d
如图所示,因为条件是相邻,所以只要与四个方向点(x',y')对应的路径建边
如何建边?要使选择了(x,y)对应路径的第k号路的相邻路径只能选第[k-d,k+d]条
只要加边(nu[i][j][k],nu[x][y][k-d],inf) 这条边不能被割
如果割边选的是第[1,k-d-1]或[k+d+1,r]流都会通过新加边到达T,与割定义不符
所以在这样的模型下跑一边最大流就行了
给出一种减少超时的方法(见下红代码)
解释:
事先复制head数组(链式前向星)为cur,之后重点在“&”号上面,它会使cur随i的变化而变化
也就是说,这条边遍历了,求出了流,下一次就会跳过遍历过的边
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 struct Node 8 { 9 int next,to,dis; 10 }edge[2000001]; 11 int num,head[1000001],n,m,d,r,nu[51][51][51],dist[1000001],cnt,ans,cur[1000001]; 12 const int dx[5]={0,1,-1,0,0}; 13 const int dy[5]={0,0,0,1,-1}; 14 void add(int u,int v,int dis) 15 { 16 //cout<<u<<' '<<v<<' '<<dis<<endl; 17 edge[num].next=head[u]; 18 edge[num].to=v; 19 edge[num].dis=dis; 20 head[u]=num++; 21 22 edge[num].next=head[v]; 23 edge[num].to=u; 24 edge[num].dis=0; 25 head[v]=num++; 26 } 27 bool bfs(int S,int T) 28 { 29 int i; 30 memset(dist,-1,sizeof(dist)); 31 queue<int>Q; 32 Q.push(S); 33 dist[S]=1; 34 while (!Q.empty()) 35 { 36 int u=Q.front(); 37 Q.pop(); 38 for (i=head[u];i!=-1;i=edge[i].next) 39 { 40 int v=edge[i].to; 41 if (edge[i].dis>0&&dist[v]==-1) 42 { 43 dist[v]=dist[u]+1; 44 Q.push(v); 45 } 46 } 47 } 48 if (dist[T]==-1) return 0; 49 return 1; 50 } 51 int dfs(int x,int flow,int des) 52 { 53 int res=0; 54 //cout<<x<<endl; 55 if (flow<=0||x==des) return flow; 56 for (int &i=cur[x];i!=-1;i=edge[i].next) 57 { 58 if (dist[edge[i].to]==dist[x]+1&&edge[i].dis>0) 59 { 60 int tmp=dfs(edge[i].to,min(flow-res,edge[i].dis),des); 61 if (tmp<=0) continue; 62 edge[i^1].dis+=tmp; 63 edge[i].dis-=tmp; 64 res+=tmp; 65 if (res==flow) return res; 66 } 67 } 68 return res; 69 } 70 int main() 71 {int k,i,j,p,v; 72 cin>>n>>m>>r; 73 cnt=0; 74 cin>>d; 75 memset(head,-1,sizeof(head)); 76 for (k=1;k<=r+1;k++) 77 { 78 for (i=1;i<=n;i++) 79 { 80 for (j=1;j<=m;j++) 81 { 82 nu[i][j][k]=++cnt; 83 } 84 } 85 } 86 87 for (i=1;i<=n;i++) 88 { 89 for (j=1;j<=m;j++) 90 add(0,nu[i][j][1],2e9),add(nu[i][j][r+1],cnt+1,2e9); 91 } 92 93 for (k=1;k<=r;k++) 94 { 95 for (i=1;i<=n;i++) 96 { 97 for (j=1;j<=m;j++) 98 { 99 scanf("%d",&v); 100 add(nu[i][j][k],nu[i][j][k+1],v); 101 } 102 } 103 } 104 for (i=d+1;i<=r;i++) 105 { 106 for (j=1;j<=n;j++) 107 { 108 for (k=1;k<=m;k++) 109 { 110 for (p=1;p<=4;p++) 111 { 112 int x=j+dx[p],y=k+dy[p]; 113 if (x&&y&&x<=n&&y<=m) 114 { 115 add(nu[j][k][i],nu[x][y][i-d],2e9); 116 } 117 } 118 } 119 } 120 } 121 while (bfs(0,cnt+1)) 122 { 123 int a=0; 124 memcpy(cur,head,sizeof(cur)); 125 while (a=dfs(0,2e9,cnt+1)) ans+=a; 126 } 127 cout<<ans; 128 }