[BZOJ3144][HNOI2013]切糕

题面戳我

题目描述

经过千辛万苦小 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)。一个合法的切面满足以下两个条件:
1、与每个纵轴(一共有 P*Q 个纵轴)有且仅有一个交点。即切面是一个函数 f(x,y),对于所有 1≤x≤P, 1≤y≤Q,我们需指定一个切割点 f(x,y),且 1≤f(x,y)≤R。
2、切面需要满足一定的光滑性要求,即相邻纵轴上的切割点不能相距太远。对于所有的 1≤x,x’≤P 和 1≤y,y’≤Q,若|x-x’|+|y-y’|=1,则|f(x,y)-f(x’,y’)| ≤D,其中 D 是给定的一个非负整数。 可能有许多切面f 满足上面的条件,小A 希望找出总的切割点上的不和谐值最小的那个。

输入格式:

第一行是三个正整数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

题解

下文中我们用三元组\((x,y,z)\)表示第\(z\)层第\(x\)行第\(y\)列的点。
新建源点汇点,源点向第一层的每个点连边,容量为\(inf\);然后,每个点向其下一层对应的点(最后一层向汇点)连边,即\((x,y,z)\)\(z<R?(x,y,z+1):t\)连边,容量为\(v(x,y,z)\)
已知这样的图跑出最小割也就是最大流就是不加条件限制2的答案。所以我们就要处理限制2。
我们要求相邻两个位置割的位置差不超过\(D\),从\((x,y,z)(z>D)\)连向\((x-1,y,z-D),(x,y-1,z-D),(x+1,y,z-D),(x,y+1,z-D)\),都是容量\(inf\)的边,保证了如果选出来的两个位置差大于\(D\)的情况下不可能是满流。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 100005;
const int inf = 1e9;
struct edge{int to,next,w;}a[N*10];
int P,Q,R,D,v[41][41][41],s,t,head[N],cnt=1,dep[N],cur[N];
queue<int>QQ;
int gi()
{
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
int pos(int z,int x,int y)
{
	return (z-1)*P*Q+(x-1)*Q+y;
}
void link(int u,int v,int w)
{
	a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
	a[++cnt]=(edge){u,head[v],0};head[v]=cnt;
}
bool bfs()
{
	memset(dep,0,sizeof(dep));
	dep[s]=1;QQ.push(s);
	while (!QQ.empty())
	{
		int u=QQ.front();QQ.pop();
		for (int e=head[u];e;e=a[e].next)
			if (a[e].w&&!dep[a[e].to])
				dep[a[e].to]=dep[u]+1,QQ.push(a[e].to);
	}
	return dep[t];
}
int dfs(int u,int flow)
{
	if (u==t)
		return flow;
	for (int &e=cur[u];e;e=a[e].next)
		if (a[e].w&&dep[a[e].to]==dep[u]+1)
		{
			int temp=dfs(a[e].to,min(a[e].w,flow));
			if (temp) {a[e].w-=temp;a[e^1].w+=temp;return temp;}
		}
	return 0;
}
int Dinic()
{
	int res=0;
	while (bfs())
	{
		for (int i=t;i;i--) cur[i]=head[i];
		while (int temp=dfs(s,inf)) res+=temp;
	}
	return res;
}
int main()
{
	P=gi();Q=gi();R=gi();D=gi();s=P*Q*R+1;t=s+1;
	for (int z=1;z<=R;z++)
		for (int x=1;x<=P;x++)
			for (int y=1;y<=Q;y++)
				v[z][x][y]=gi();
	for (int x=1;x<=P;x++)
		for (int y=1;y<=Q;y++)
			link(s,pos(1,x,y),inf);
	for (int z=1;z<=R;z++)
		for (int x=1;x<=P;x++)
			for (int y=1;y<=Q;y++)
				link(pos(z,x,y),z<R?pos(z+1,x,y):t,v[z][x][y]);
	for (int x=1;x<=P;x++)
		for (int y=1;y<=Q;y++)
		{
			if (x>1)
				for (int z=D+1;z<=R;z++)
					link(pos(z,x,y),pos(z-D,x-1,y),inf);
			if (y>1)
				for (int z=D+1;z<=R;z++)
					link(pos(z,x,y),pos(z-D,x,y-1),inf);
			if (x<P)
				for (int z=D+1;z<=R;z++)
					link(pos(z,x,y),pos(z-D,x+1,y),inf);
			if (y<Q)
				for (int z=D+1;z<=R;z++)
					link(pos(z,x,y),pos(z-D,x,y+1),inf);
		}
	printf("%d\n",Dinic());
	return 0;
}
posted @ 2017-12-29 16:17  租酥雨  阅读(302)  评论(0编辑  收藏  举报