1. 程式人生 > 實用技巧 >P3227 [HNOI2013]切糕 最小割

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\to(x_1,y_1,z_1)\to (x_2,y_2,z_1-d)\to(x_2,y_2,r+1)\to 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\n",ans);
	}

}

int main()
{
	zzc::work();
	return 0;
}