bzoj3144 [Hnoi2013]切糕
3144: [Hnoi2013]切糕
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2380 Solved: 1301
[Submit][Status][Discuss]
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
分析:一道我不会但是很经典的套路题.
一开始的想法是dp,但是要记录的状态实在是太多了,复杂度太高.
然后想到的是贪心.但是想不到贪心的方法.题目牵扯到的条件太多,没有办法贪心.
怎么办呢?网络流!
对于网络流题目的思考方法:先考虑最大流模型,再来考虑费用流模型. 如果有限制则考虑容量限制和最小割限制.
先尝试用最大流.怎么表示限制呢? 容量限制?联想到流量平衡模型(传送门).但是这道题根本就没办法按照那个模型的套路来.那就只有考虑最小割限制咯.
怎么用最小割来表示限制呢?一般连边权为inf的边使得不会割掉这条边. 因为最小割最终要使得源点和汇点不连通,可以利用这一条性质.
考虑本题的限制,先把绝对值给去掉,每个点只考虑四周比它高度小的点,也就是f(x,y) - f(x',y') ≤ D. 那么我们的任务就是割掉一条f(x,y)的边后,割掉f(x',y') (f(x',y')不满足要求)后源点和汇点仍然连通. 怎么做到这一点呢?通过加边权为inf的边就能做到了.
观察这张图. 这是一个立体结构. 如果割掉红色的边,再割掉绿色的边,ST还可以连通,要的就是这个效果. 如果不割绿边,而是割绿边上面的边,ST就不连通了.正好对应了限制.
那么建图方法就出来了:
1.从S点向第1层的所有点连容量为inf的边.
2.从(i,j)的第k层连(i,j)的第k+1层容量为f(i,j,k)的边,
3.从第h + 1层的所有点向T连容量为inf的边.
4.从(i,j,k)向(i',j',k - D)连容量为inf的边.
所求的最小割即是答案.
这是一类经典模型:解决这类题目的方法就是通过最小割满足限制.
#include <cstdio> #include <queue> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int inf = 0x7fffffff,maxn = 300010,dx[] = {0,0,1,-1},dy[] = {1,-1,0,0}; int n,m,h,id[45][45][45],cnt,S,T,head[maxn],to[maxn],nextt[maxn],w[maxn],tot = 2; int d[maxn],D,ans; void add(int x,int y,int z) { w[tot] = z; to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; w[tot] = 0; to[tot] = x; nextt[tot] = head[y]; head[y] = tot++; } void build() { for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) add(S,id[1][i][j],inf); for (int k = 1; k <= h; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { int x; scanf("%d",&x); add(id[k][i][j],id[k + 1][i][j],x); } for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) add(id[h + 1][i][j],T,inf); for (int k = D + 1; k <= h + 1; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) for (int p = 0; p < 4; p++) { int nx = dx[p] + i,ny = dy[p] + j; if (id[k - D][nx][ny]) add(id[k][i][j],id[k - D][nx][ny],inf); } } bool bfs() { memset(d,-1,sizeof(d)); d[S] = 0; queue <int> q; q.push(S); while (!q.empty()) { int u = q.front(); q.pop(); if (u == T) return true; for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (w[i] && d[v] == -1) { d[v] = d[u] + 1; q.push(v); } } } return false; } int dfs(int u,int f) { if (u == T) return f; int res = 0; for (int i = head[u];i;i = nextt[i]) { int v = to[i]; if (w[i] && d[v] == d[u] + 1) { int temp = dfs(v,min(f - res,w[i])); w[i] -= temp; w[i ^ 1] += temp; res += temp; if (res == f) return res; } } if (!res) d[u] = -1; return res; } void dinic() { while(bfs()) ans += dfs(S,inf); } int main() { scanf("%d%d%d",&n,&m,&h); scanf("%d",&D); for (int i = 1; i <= h + 1; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= m; k++) id[i][j][k] = ++cnt; S = ++cnt; T = ++cnt; build(); dinic(); printf("%d\n",ans); return 0; }