题目描述
经过千辛万苦小 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 希望找出总的切割点上的不和谐值最小的那个。
解析:
其实题目描述那么长,他的意思不过是:有很多根杆子,每根都有R个点,每个点有一个权值,在每根上选一个点,相邻杆子的点的高度差不超过D,求最小权值和。这道题的建模比较神奇,考虑最小割。一共P*Q*R个点,从P*Q个R-1层到R层的点连边代表选择这一层的权值;加一个0层,S到0层连INF,R层到T连INF,相邻相差超过D的层连INF,这样最小割(最大流)就代表每个方格必须选一个层,相邻方格差不超过D的最小代价。怎么想到的这么建的,其实很简单,因为每根只能选一个点,一定要把每根上两点之间连上边权,但原图上的边是不够把R条边插进去的,自然而然就想到再建一层。考虑到距离限制,我们让相邻相差超过D的层连INF,这样这条边就割不掉啦,然后就解决啦~。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 1e9
#define MAXN 300005
using namespace std;
queue<int> q;
struct node
{
int from,to,remain,next;
}e[MAXN*10];
int e_num=-1,point[MAXN];
int deep[MAXN],cur[MAXN];
bool vis[MAXN];
int s,t,ans,n,m;
int P,R,Q,D;
inline int get()
{
int x=0,p=1;char c;
c=getchar();
while (c<'0'||c>'9') {if (c=='-') p=-1;c=getchar();}
while (c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
return x*p;
}
inline void add(int from,int to,int remain)
{
++e_num;
e[e_num].from=from;
e[e_num].to=to;
e[e_num].remain=remain;
e[e_num].next=point[from];
point[from]=e_num;
}
bool bfs(int from,int to)
{
memset(deep,0x7f,sizeof(deep));
memset(vis,0,sizeof(vis));
for (int i=0;i<=t;i++)
cur[i]=point[i];
deep[from]=0;
vis[from]=1;
q.push(from);
while (!q.empty())
{
int u=q.front();
q.pop();
vis[u]=false;
for (int i=point[u];i!=-1;i=e[i].next)
if (deep[e[i].to]>inf&&e[i].remain)
{
deep[e[i].to]=deep[u]+1;
if (!vis[e[i].to])
{
q.push(e[i].to);
vis[e[i].to]=true;
}
}
}
return deep[to]<inf;
}
int dfs(int from,int to,int limit)
{
int f,flow=0;
if (!limit) return 0;
if (from==to) return limit;
for (int i=cur[from];i!=-1;i=e[i].next)
{
cur[from]=i;
if (deep[e[i].to]==deep[from]+1&&(f=dfs(e[i].to,to,min(limit,e[i].remain))))
{
flow+=f;
limit-=f;
e[i].remain-=f;
e[i^1].remain+=f;
if (!limit) break;
}
}
return flow;
}
void dinic()
{
while (bfs(s,t))
ans+=dfs(s,t,inf);
}
void addedge(int x,int y,int z)
{
add(x,y,z);
add(y,x,0);
}
int calc(int x,int y,int z)
{
return (x-1)*P*Q+(y-1)*Q+z;
}
int main()
{
int x,cnt=0;
memset(point,-1,sizeof(point));
P=get();Q=get();R=get();D=get();
s=0;t=(P+1)*(Q+1)*(R+1)+100;
for (int i=1;i<=R;i++)
for (int j=1;j<=P;j++)
{
for (int k=1;k<=Q;k++)
{
if (i==1) addedge(s,calc(1,j,k),inf);
if (i==R) addedge(calc(R+1,j,k),t,inf);
x=get();
addedge(calc(i,j,k),calc(i+1,j,k),x);
}
}
for (int i=D+1;i<=R;i++)
for (int j=1;j<=P;j++)
for (int k=1;k<=Q;k++)
{
if (j+1<=P)
addedge(calc(i,j,k),calc(i-D,j+1,k),inf);
if (j-1>=1)
addedge(calc(i,j,k),calc(i-D,j-1,k),inf);
if (k+1<=Q)
addedge(calc(i,j,k),calc(i-D,j,k+1),inf);
if (k-1>=1)
addedge(calc(i,j,k),calc(i-D,j,k-1),inf);
}
dinic();
printf("%d",ans);
}