BZOJ3144【HNOI2013】切糕

1 篇文章 0 订阅
1 篇文章 0 订阅

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3144


【分析】

    所有人都说这是个经典的最小割模型......蒟蒻泪流满面......

    建一个r+1层的,每层都是p*q的图,从源出发向第一层连inf的边,从第r+1层上每个点出发向汇连inf的边,对i,j,k与i,j,k+1之间连v(i,j,k)的边。

    如果没有高度限制,那么这么做就是在每一条纵轴上选一个点。

    因为有高度限制,所以对于每个层数大于d的点,要向x,y坐标相邻的,层数小d的点连一条inf的边。

    以p=1,q=2,r=5,d=2为例。(先忽略从右边较高的点连向左边较低的点的那些边)

    

    下面是源,上面是汇。如图所示,如果我把图中的红边割掉,那么图中的蓝边也就没有用了。如果此时把绿边割掉,那么还是存在一条从源到汇的路径;如果割绿色的边上面的边,那么源到汇就没有路径可达了。

    仔细理解一下,也就是在左边选了一条边以后,右边的高度小于当前边的边就不能选了。如果把右上向左下连的边也连起来,那么高度就完全限制住了。

    然后跑一下最大流就好了。


【代码】

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxl=45,maxn=75000,maxm=maxn<<4,inf=~0U>>1,mov[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

int head[maxn],next[maxm],E[maxm],F[maxm],Ecnt;
int id[maxl][maxl][maxl];
int p,q,r,d;
int s,e;

inline void Add_Edge(int x,int y,int f) {next[++Ecnt]=head[x];head[x]=Ecnt;E[Ecnt]=y;F[Ecnt]=0;next[++Ecnt]=head[y];head[y]=Ecnt;E[Ecnt]=x;F[Ecnt]=f;}

void Init()
{
	scanf("%d%d%d%d",&p,&q,&r,&d);
	int cnt=0,x;
	Ecnt=1;
	s=++cnt;
	for (int i=1;i<=r+1;i++)
	for (int j=1;j<=p  ;j++)
	for (int k=1;k<=q  ;k++)
	id[i][j][k]=++cnt;
	e=++cnt;
	
	for (int i=1;i<=r;i++)
	for (int j=1;j<=p;j++)
	for (int k=1;k<=q;k++)
	scanf("%d",&x),Add_Edge(id[i][j][k],id[i+1][j][k],x);
	
	for (int i=1;i<=p;i++)
	for (int j=1;j<=q;j++)
	Add_Edge(s,id[1][i][j],inf),Add_Edge(id[r+1][i][j],e,inf);
	
	#define nx (j+mov[mv][0])
	#define ny (k+mov[mv][1])
	for (int i=d+1;i<=r+1;i++)
	for (int j=1;j<=p;j++)
	for (int k=1;k<=q;k++)
	for (int mv=0;mv<4;mv++)
	if (id[i][nx][ny])
	Add_Edge(id[i][j][k],id[i-d][nx][ny],inf);
	#undef nx
	#undef ny
}

int Q[maxn],Vis[maxn],h[maxn];
bool BFS(int time)
{
	int top=1,tail=1;
	Q[1]=s;Vis[1]=time;h[1]=0;
	while (top<=tail)
	{
		int x=Q[top++],hx=h[x];
		for (int i=head[x];i;i=next[i]) if (Vis[E[i]]!=time && F[i^1])
			h[E[i]]=h[x]+1,Vis[E[i]]=time,Q[++tail]=E[i];
		if (Vis[e]==time) break;
	}
	return Vis[e]==time;
}

int DFS(int x,int f)
{
	if (x==e) return f;
	int res=f;
	for (int i=head[x];i && res;i=next[i]) if (h[E[i]]==h[x]+1 && F[i^1])
	{
		int d=DFS(E[i],min(F[i^1],res));
		res-=d;F[i]+=d;F[i^1]-=d;
	}
	if (f==res) h[x]=-1;
	return f-res;
}

void Dinic()
{
	int res=0,time=0;
	while (BFS(++time)) res+=DFS(s,inf);
	printf("%d\n",res);
}

int main()
{
	Init();
	Dinic();
	return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值