bzoj-3144 切糕

141 篇文章 0 订阅
44 篇文章 0 订阅

题意:

给出一个R*P*Q的三维点阵,求一个函数f(x,y)  (1<=x<=P,1<=y<=Q);

使∑v[f(x,y)][x][y]最小,且相邻两个f值之差不超过D;

R,P,Q<=40,v<=1000;


题解:
看起来就好神的题,然而就算告诉我是网络流我也不信能跑64000个点;

不过事实上= =,数据中最大是30 30 30的。。所以结果大家跑的都很快;

这道题的建图就是首先限制在一行只能选择一个,那么从上向下连出一条链,每个弧的流量都是对应点的权值;

如果不考虑D的影响,对这个求最小割得到的就是答案啦;

为了加入D这个条件,我们对于每个点再向它四周比他高D的点连一条流量为INF的边;

这样之后一定还能保证每个(x,y)一定是一个割边,因为假设一个(x,y)有两个割边;

那么一定有一个流进入到它们中间,否则下面的割边没有意义;

而因为如果上面的割边有意义,那么一定是为了防止一个从他们中间出发向上的流;

实际上这样防止是没有用的,因为那个流入它们中间的流可以向上流走;

所以这种情况不可能存在,那么根据这个结论,一个合法的个割集也就一定满足D的条件了;

时间复杂度呵呵;


代码:


#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 50
#define PT 70000
#define M 1000000
using namespace std;
int P,Q,R,D;
int next[M],to[M],flow[M],head[PT],ce=1;
int dis[PT],S,T;
int v[N][N][N],num[N][N][N],tot;
queue<int>q;
void add(int x,int y,int fl)
{
	to[++ce]=y;
	next[ce]=head[x];
	flow[ce]=fl;
	head[x]=ce;
	to[++ce]=x;
	next[ce]=head[y];
	flow[ce]=0;
	head[y]=ce;
}
void add(int x,int y)
{
	to[++ce]=y;
	next[ce]=head[x];
	flow[ce]=0x3f3f3f3f;
	head[x]=ce;
	to[++ce]=x;
	next[ce]=head[y];
	flow[ce]=0;
	head[y]=ce;
}
bool BFS()
{
	memset(dis,0,sizeof(dis));
	q.push(S);
	dis[S]=1;
	int x,i;
	while(!q.empty())
	{
		x=q.front(),q.pop();
		for(i=head[x];i;i=next[i])
		{
			if(flow[i]&&!dis[to[i]])
			{
				dis[to[i]]=dis[x]+1;
				q.push(to[i]);
			}
		}
	}
	return dis[T]!=0;
}
int dfs(int x,int lim)
{
	if(x==T)
		return lim;
	int i,ret,temp;
	for(i=head[x],ret=0;i;i=next[i])
	{
		if(flow[i]&&dis[to[i]]==dis[x]+1)
		{
			temp=dfs(to[i],min(flow[i],lim-ret));
			flow[i]-=temp,flow[i^1]+=temp;
			ret+=temp;
			if(lim==ret)
				return ret;
		}
	}
	if(!ret)	dis[x]=0;
	return ret;
}
int main()
{
	int i,j,k,x,y,ans;
	scanf("%d%d%d%d",&P,&Q,&R,&D);
	for(i=1;i<=R;i++)
	{
		for(j=1;j<=P;j++)
		{
			for(k=1;k<=Q;k++)
			{
				scanf("%d",&v[i][j][k]);
				num[i][j][k]=++tot;
			}
		}
	}
	S=tot+1,T=tot+2;
	for(i=1;i<=R;i++)
	{
		for(j=1;j<=P;j++)
		{
			for(k=1;k<=Q;k++)
			{
				if(i==1)
					add(S,num[i][j][k],v[i][j][k]);
				else
					add(num[i-1][j][k],num[i][j][k],v[i][j][k]);
				if(i==R)
					add(num[i][j][k],T);
				if(i>D)
				{
					if(j!=1)	add(num[i][j][k],num[i-D][j-1][k]);
					if(j!=P)	add(num[i][j][k],num[i-D][j+1][k]);
					if(k!=1)	add(num[i][j][k],num[i-D][j][k-1]);
					if(k!=Q)	add(num[i][j][k],num[i-D][j][k+1]);
				}
			}
		}
	}
	ans=0;
	while(BFS())
		ans+=dfs(S,0x3f3f3f3f);
	printf("%d\n",ans);
	return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值