BZOJ3144 [Hnoi2013]切糕 【最小割】
题目
输入格式
第一行是三个正整数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。
输出格式
仅包含一个整数,表示在合法基础上最小的总不和谐值。
输入样例
2 2 2
1
6 1
6 1
2 6
2 6
输出样例
6
提示
最佳切面的f为f(1,1)=f(2,1)=2,f(1,2)=f(2,2)=1
题解
论一类最小割模型:
有P * Q个纵列,每列选一个点,且相邻纵列之间的点距离不超过D,求选点最小值
我们将每一列所有点向其下一个点连边【这时候多加上额外的一层点】,容量为其权值,这样由最小割,每一类都会选择一条边割去,就意味着选了这条边入度的点
为了满足相邻距离不超过D的限制,我们对(x,y,z)向相邻的(x’,y’,z - D)连边INF,使得在选择(x,y,z)后必须选择(x’,y’,z - D)以上的点,如图
如此建图,跑最大流
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
#define cls(x) memset(x,0,sizeof(x))
#define p(x,y,z) ((z - 1) * P * Q + (x - 1) * Q + y)
using namespace std;
const int maxn = 100000,maxm = 3000005,INF = 1000000000;
inline int RD(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
return out * flag;
}
int P,Q,R,D;
int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T;
struct EDGE{int to,f,nxt;}ed[maxm];
inline void build(int u,int v,int w){
ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;
ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;
}
bool bfs(){
for (int i = S; i <= T; i++) vis[i] = d[i] = 0;
queue<int> q;
d[S] = 0; vis[S] = true; q.push(S); int u,to;
while (!q.empty()){
u = q.front(); q.pop();
Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
d[to] = d[u] + 1; vis[to] = true; q.push(to);
}
}
return vis[T];
}
int dfs(int u,int minf){
if (u == T || !minf) return minf;
int flow = 0,f,to;
if (cur[u] == -2) cur[u] = h[u];
for (int& k = cur[u]; k != -1; k = ed[k].nxt)
if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){
ed[k].f -= f; ed[k ^ 1].f += f;
minf -= f; flow += f;
if (!minf) break;
}
return flow;
}
int maxflow(){
int flow = 0;
while (bfs()){
for (int i = S; i <= T; i++) cur[i] = -2;
flow += dfs(S,INF);
}
return flow;
}
int X[4] = {0,0,-1,1},Y[4] = {-1,1,0,0};
int main(){
memset(h,-1,sizeof(h));
P = RD(); Q = RD(); R = RD(); D = RD(); S = 0; T = P * Q * (R + 1) + 1;
int v;
REP(x,P) REP(y,Q) build(S,p(x,y,1),INF),build(p(x,y,R + 1),T,INF);
REP(z,R) REP(x,P) REP(y,Q){
v = RD();
build(p(x,y,z),p(x,y,z + 1),v);
if (z - D > 0){
for (int k = 0; k < 4; k++){
int nx = x + X[k],ny = y + Y[k];
if (nx < 1 || ny < 1 || nx > P || ny > Q) continue;
build(p(x,y,z),p(nx,ny,z - D),INF);
}
}
}
printf("%d",maxflow());
return 0;
}