P3227 [HNOI2013]切糕 最小割

题意:

戳这里

分析:

又一次被题解的智慧所折服,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;
}

原文地址:https://www.cnblogs.com/youth518/p/14265640.html