zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    OVS local network 连通性分析
    再部署一个 instance 和 Local Network
    ARM Linux系统调用的原理
    Linux内存管理中的slab分配器
    浅析基于ARM的Linux下的系统调用的实现
    分享一个查看十六进制的位的小程序
    mokoid 源代码下载
    shell中eval的使用问题
    批量去掉文件中的^M
    基于TINY4412的Andorid开发-------简单的LED灯控制
  • 原文地址:https://www.cnblogs.com/youth518/p/14265640.html
Copyright © 2011-2022 走看看