zoukankan      html  css  js  c++  java
  • [HNOI 2013] 切糕

    题目

    点这里看题目。

    分析

    这道题真的很经典。

    如果没有光滑性的限制,我们发现这是个弱智贪心最小割问题。每一个位置切割的代价可以转移为边的容量。

    因此我们可以很容易地建图,除去源汇共 (PQ(R+1)) 个点,用 ((a,b,c)) 表示第 (a) 层上第 (b) 行第 (c) 列的点。

    于是, (v(x,y,z)) 转化为 ((z,x,y) ightarrow (z+1,x,y)) ,容量为 (v(x,y,z)) 。剩下的还需要连接源汇,就略去了。

    现在考虑光滑度的限制。可以发现:

    [|f(x,y)-f(x',y')|le DLeftrightarrow f(x',y')ge f(x,y)-Dland f(x,y)ge f(x',y')-D ]

    ((x,y)) 这个位置上我们可以只考虑 (f(x',y')ge f(x,y)-D) 的限制。这就意味着,如果我们割掉了 ((z,x,y) ightarrow (z+1,x,y)) 这条边,那么 ((z-k-1,x',y') ightarrow (z-k,x',y')(Dle kle z-1)) 的边都是碰不得的。根据割的含义,也就是如果我们此时割掉了 ((z-k-1,x',y') ightarrow (z-k,x',y')) ,我们就必须要让 (s) 可以到达 (t) 。于是可以想到构造方案:连接 ((z,x,y) ightarrow (z-D,x',y')) 。这样,如果割掉了不合法的边,一定有一条合法路径: (s ightarrow (z,x,y) ightarrow (z-D,x',y') ightarrow t)

    小结:

    根据不合法的情况,建立 (+infty) 的边来构造 (s ightarrow t) 的路径,绝对是最小割最常用的方法之一

    代码

    #include <cstdio>
    
    const int INF = 0x3f3f3f3f;
    const int MAXN = 2e5 + 5, MAXM = 2e5 + 5;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0; char s = getchar(); int f = 1;
    	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ); s = getchar(); }
    	x *= f;
    }
    
    template<typename _T>
    void write( _T x )
    {
    	if( x < 0 ) putchar( '-' ), x = -x;
    	if( 9 < x ) write( x / 10 );
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    _T MIN( const _T a, const _T b )
    {
    	return a < b ? a : b;
    }
    
    struct Edge
    {
    	int to, nxt, c;
    }Graph[MAXM << 1];
    
    int dir[4][2] = { { -1, 0 }, { 1, 0 }, { 0, 1 }, { 0, -1 } };
    
    int q[MAXN];
    
    int head[MAXN], dep[MAXN], cur[MAXN];
    int P, Q, R, D, cnt = 1, tot;
    
    void AddEdge( const int from, const int to, const int C )
    {
    	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    	Graph[cnt].c = C, head[from] = cnt;
    }
    
    #define ID( i, j, k ) ( ( i - 1 ) * P * Q + ( j - 1 ) * Q + k )
    
    bool Inside( const int &x, const int &y ) { return 1 <= x && x <= P && 1 <= y && y <= Q; }
    void AddE( const int from, const int to, const int C ) { AddEdge( from, to, C ), AddEdge( to, from, 0 ); }
    
    bool BFS( const int S, const int T )
    {
    	int h = 1, t = 0, u, v;
    	for( int i = 1 ; i <= tot ; i ++ ) dep[i] = INF;
    	dep[q[++ t] = S] = 0;
    	while( h <= t )
    	{
    		u = q[h ++];
    		for( int i = head[u] ; i ; i = Graph[i].nxt )
    			if( Graph[i].c && dep[v = Graph[i].to] > dep[u] + 1 )
    				dep[q[++ t] = v] = dep[u] + 1;
    	}
    	return dep[T] < INF;
    }
    
    int DFS( const int u, const int lin, const int T )
    {
    	if( u == T ) return lin;
    	int used = 0, ret, v, c;
    	for( int &i = cur[u] ; i ; i = Graph[i].nxt )
    	{
    		v = Graph[i].to, c = Graph[i].c;
    		if( dep[v] == dep[u] + 1 && c && ( ret = DFS( v, MIN( lin - used, c ), T ) ) )
    		{
    			used += ret, Graph[i].c -= ret, Graph[i ^ 1].c += ret;
    			if( used == lin ) break;
    		}
    	}
    	if( used < lin ) dep[u] = INF;
    	return used;
    }
    
    int Dinic( const int S, const int T )
    {
    	int f = 0;
    	while( BFS( S, T ) )
    	{
    		for( int i = 1 ; i <= tot ; i ++ ) cur[i] = head[i];
    		f += DFS( S, INF, T );
    	}
    	return f;
    }
    
    int main()
    {
    	read( P ), read( Q ), read( R ), read( D );
    	tot = ( R + 1 ) * P * Q; const int s = ++ tot, t = ++ tot;
    	for( int i = 1 ; i <= R ; i ++ )
    		for( int j = 1 ; j <= P ; j ++ )
    			for( int k = 1, c ; k <= Q ; k ++ )
    				 read( c ), AddE( ID( i, j, k ), ID( i + 1, j, k ), c );
    	for( int j = 1 ; j <= P ; j ++ )
    		for( int k = 1 ; k <= Q ; k ++ )
    			AddE( s, ID( 1, j, k ), INF ),
    			AddE( ID( R + 1, j, k ), t, INF );
    	for( int j = 1 ; j <= P ; j ++ )
    		for( int k = 1 ; k <= Q ; k ++ )
    			for( int i = D + 1 ; i <= R + 1 ; i ++ )
    				for( int t = 0, tx, ty ; t < 4 ; t ++ )
    					if( Inside( tx = j + dir[t][0], ty = k + dir[t][1] ) )
    						AddE( ID( i, j, k ), ID( i - D, tx, ty ), INF );
    	write( Dinic( s, t ) ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    MFC 按钮
    读写文件
    遍历一个文件夹所有文件
    Java的运行机制
    selenium学习笔记——高级操作
    selenium学习笔记——定位元素
    selenium学习笔记——介绍&环境准备
    搭建安卓系统的测试环境
    Linux下Java环境的安装与配置
    Linux的目录结构介绍
  • 原文地址:https://www.cnblogs.com/crashed/p/14207489.html
Copyright © 2011-2022 走看看