切糕
题解
以前做过的题,现在却只能想到"网络流"三个字,怎么建图都不会了。
应该很容易想到,通过最大流最小割来求出总的最小值。
首先我们对于同一列,我们至少要割掉其中一个点,我们可以将这
R
R
R个点都连成一列,从
(
i
,
j
,
k
)
−
>
(
i
,
j
,
k
+
1
)
(i,j,k)->(i,j,k+1)
(i,j,k)−>(i,j,k+1)连一条流量为
v
a
l
i
,
j
,
k
val_{i,j,k}
vali,j,k的边,割掉这条边就表示在这一列选择割点
(
i
,
j
,
k
)
(i,j,k)
(i,j,k)。
这样形成的一条链,接在
S
S
S与
T
T
T间,显然,在这一条链,肯定会有一条边被割掉,也只会割一条。
我们继续考虑对于相邻列,它们之间是不能选择距离超过
d
d
d的点的。
如果我们在某一列上割了点
x
x
x,那么另一列中就得从
[
x
−
d
,
x
+
d
]
[x-d,x+d]
[x−d,x+d]中选一个点割掉,显然直接这样是不大好限制的,我们可以将这个限制转化一下。
转化成我们在一列的
[
1
,
x
]
[1,x]
[1,x]与另一列的
[
x
−
d
,
r
]
[x-d,r]
[x−d,r]中至少要选一个。
否则的话就意味着我们两个点的距离是一定超过
d
d
d的。
当
x
x
x取遍
[
x
,
r
]
[x,r]
[x,r],且每一列都有这样的限制时,可以证明,是符合我们之前的条件的。
我们不妨设我们在该列上取了
x
x
x,在另一列上去了
y
y
y。
如果
y
>
x
+
d
y>x+d
y>x+d,那么就存在
S
−
>
y
−
>
(
y
−
d
>
x
)
−
>
T
S->y->(y-d>x)->T
S−>y−>(y−d>x)−>T的一条联通路径,显然是不合法的。
当
y
<
x
−
d
y<x-d
y<x−d时,也同样存在
S
−
>
x
−
>
(
x
−
d
>
y
)
−
>
T
S->x->(x-d>y)->T
S−>x−>(x−d>y)−>T的一条联通路径,同样是不合发的。
实际上的连边我们从
(
i
,
j
,
k
)
−
>
(
i
+
d
x
,
j
+
d
y
,
k
−
d
)
(i,j,k)->(i+dx,j+dy,k-d)
(i,j,k)−>(i+dx,j+dy,k−d)连一条流量为
I
N
F
INF
INF的边就行了。
显然,我们建出图后跑一遍网络流求出最小割就行了。
时间复杂度
O
(
P
3
Q
3
R
3
)
O\left(P^3Q^3R^3\right)
O(P3Q3R3),反正网络流的时间复杂度又不重要。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 70005
#define MAXM 400005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int inv2=5e8+4;
const int jzm=23333;
const int zero=20000;
const int lim=1000000;
const int orG=3,ivG=334845270;
const double Pi=acos(-1.0);
const double eps=1e-12;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int P,Q,R,D,head[MAXN],tot,val[45][45][45],id[45][45][45],S,T,cnt;
int dx[5]={1,-1,0,0},dy[5]={0,0,1,-1},dis[MAXN],cur[MAXN];
queue<int>q;
struct edge{int to,nxt,flow,op;}e[MAXM<<1];
void addEdge(int u,int v,int f){e[++tot]=(edge){v,head[u],f};head[u]=tot;}
void addedge(int u,int v,int f){
addEdge(u,v,f);e[tot].op=tot+1;
addEdge(v,u,0);e[tot].op=tot-1;
}
bool bfs(){
for(int i=1;i<=cnt;i++)dis[i]=0,cur[i]=head[i];
while(!q.empty())q.pop();dis[S]=1;q.push(S);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u],v;i;i=e[i].nxt)
if(e[i].flow&&!dis[v=e[i].to]){
dis[v]=dis[u]+1,q.push(v);
if(v==T)return 1;
}
}
return 0;
}
int dfs(int u,int maxf){
if(u==T)return maxf;int res=0;
for(int i=cur[u];i;cur[u]=i=e[i].nxt){
int v=e[i].to;if(dis[v]!=dis[u]+1||!e[i].flow)continue;
int tmp=dfs(v,min(maxf,e[i].flow));
e[i].flow-=tmp;e[e[i].op].flow+=tmp;maxf-=tmp;res+=tmp;
if(!maxf)break;
}
return res;
}
int sakura(){int res=0;while(bfs())res+=dfs(S,INF);return res;}
signed main(){
read(P);read(Q);read(R);read(D);S=++cnt;T=++cnt;
for(int k=1;k<=R;k++)
for(int i=1;i<=P;i++)
for(int j=1;j<=Q;j++)read(val[i][j][k]);
for(int i=1;i<=P;i++)
for(int j=1;j<=Q;j++)
for(int k=0;k<=R;k++)id[i][j][k]=++cnt;
for(int i=1;i<=P;i++)
for(int j=1;j<=Q;j++){
addedge(S,id[i][j][0],INF);
for(int k=1;k<=R;k++)addedge(id[i][j][k-1],id[i][j][k],val[i][j][k]);
for(int p=0;p<4;p++){
int x=i+dx[p],y=j+dy[p];if(x<1||y<1||x>P||y>Q)continue;
for(int k=D;k<=R;k++)addedge(id[i][j][k],id[x][y][k-D],INF);
}
addedge(id[i][j][R],T,INF);
}
printf("%d\n",sakura());
return 0;
}