题意:
分析:
又一次被题解的智慧所折服,orz
其实按着思路推过来还是挺符合逻辑的,一看数据范围这么小,要么 (DP) 要么 网络流, (DP) 不太现实状态数过多无法转移,那么就是网络瘤了
首先我们去掉毒瘤的光滑限制,也就是说任意两个相邻的列没有高度差要求,这样直接选出每一列最小的一个,像这种取最值的问题转化成网络流的建模就是最小割,建一个超级源向每一列的第一个连边流量为 (inf) ,再建一个超级汇点,每列最后一个与超级汇连边流量为 (inf) ,这样保证了源汇的边不会被割掉,由于题目给的是点值,我们将点值压到边上,((x,y,z)) 向 ((x,y,z+1)) 连一条流量为 (v(x,y,z)) 的边,然后直接求最小割就是答案
好,我们开始考虑怎么加上光滑限制,我们考虑什么情况下两个边不会被同时割掉,当且仅当,它们两所在的路径还有可能被增广的时候,也就是说我们需要保证他们两相连,且它们两还能形成一条增广路,那么在上面网络流模型的基础上我们由 ((x_1,y_1,z_1)) 向 ((x_2,y_2,z_1-d)) 连一条流量为 (inf) 的边,这样这条边不会被割掉保证了它们两一定相连,同时在它们两被割掉的时候还形成了一条新的增广路径 : (st o(x_1,y_1,z_1) o (x_2,y_2,z_1-d) o(x_2,y_2,r+1) o ed)
代码:
吐槽一句,题解代码过于毒瘤,建模看不懂/kk
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
using namespace std;
namespace zzc
{
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 1e5+5;
const int maxm = 1e6+5;
const int inf = 0x3f3f3f;
int p,q,r,st,ed,cnt=1,ans,d;
int head[maxn],dep[maxn],v[41][41][41];
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
struct edge
{
int to,nxt,w;
}e[maxm];
queue<int> qu;
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void add_edge(int u,int v,int w)
{
add(u,v,w);add(v,u,0);
}
int id(int x,int y,int z)
{
return (z-1)*p*q+(x-1)*q+y;
}
bool check(int x,int y)
{
return x>=1&&x<=p&&y>=1&&y<=q;
}
bool bfs()
{
for(int i=st;i<=ed;i++) dep[i]=-1;
qu.push(st);dep[st]=0;
while(!qu.empty())
{
int u=qu.front();qu.pop();
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(e[i].w&&dep[v]==-1)
{
dep[v]=dep[u]+1;
qu.push(v);
}
}
}
return dep[ed]!=-1;
}
int dfs(int u,int flow)
{
if(u==ed) return flow;
int used=0,w;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(e[i].w&&dep[v]==dep[u]+1)
{
w=dfs(v,min(e[i].w,flow-used));
e[i].w-=w;
e[i^1].w+=w;
used+=w;
if(used==flow) return used;
}
}
if(!used) dep[u]=-1;
return used;
}
void dinic()
{
int fl;
while(bfs())
{
fl=dfs(st,inf);
ans+=fl;
}
}
void work()
{
p=read();q=read();r=read();d=read();st=0;ed=p*q*(r+1)+1;
for(int z=1;z<=r;z++)
{
for(int x=1;x<=p;x++)
{
for(int y=1;y<=q;y++)
{
v[x][y][z]=read();
add_edge(id(x,y,z),id(x,y,z+1),v[x][y][z]);
}
}
}
for(int z=1;z<=r+1;z++)
{
for(int x=1;x<=p;x++)
{
for(int y=1;y<=q;y++)
{
if(z==1) add_edge(st,id(x,y,z),inf);
else if(z==r+1) add_edge(id(x,y,z),ed,inf);
if(z>=d+1)
{
for(int i=0;i<4;i++)
{
int tx=x+dx[i],ty=y+dy[i];
if(check(tx,ty)) add_edge(id(x,y,z),id(tx,ty,z-d),inf);
}
}
}
}
}
dinic();
printf("%d
",ans);
}
}
int main()
{
zzc::work();
return 0;
}