zoukankan      html  css  js  c++  java
  • Solution -「BJWC 2018」「洛谷 P4486」Kakuro

    (mathcal{Description})

      Link.

      有一个 (n imes m) 的网格图,其中某些格子被主对角线划成两个三角形,称这样的格子为特殊格;初始时,除了一些障碍格,所有空格子和特殊格的两个三角形内都分别填上了数字。称一个网格合法,当且仅当:

    • 对于每个特殊格左下方的三角形,若其不是障碍格,则其下方连续的空格子内数字之和为三角形内数字;
    • 对于每个特殊格右上方的三角形,若其不是障碍格,则其右方连续的空格子内数字之和为三角形内数字。

      为了使网格合法,你可以以一定代价将某个格子内的数 (+1)(-1),修改每个格子的代价是独立的,亦有一些格子不能修改。

      求最小代价。

      (n,mle30),每个空格子至少在一个左下非障碍的特殊格子下方,或在一个右上非障碍的特殊格子右方。

      我干嘛还要再概括一遍那么长的题意 qwq。

    (mathcal{Solution})

       这种行和列和的限制有一种套路的网络流建图模型:代表行限制结点连向其限制格子的入点,格子入点连向格子出点用于体现一些代价之类的限制,最后格子出点连向限制该格子的代表列限制的结点。

      本题,(+1/-1) 并不方便用边上的费用体现,对于一个初始数字为 (v),修改代价为 (w) 的空格子 (c),考虑如下建边:

    [(c,c',[v,v],0)\ (c',c,[0,f),w)\ (c,c',[0,+infty],w) ]

      其中 ((u,v,[l,r],w)) 表示一条从 (u)(v),流量限制为 ([l,r]),费用为 (w) 的边。发现我们通过构造第一条边“必选”使 (c) 取到初始的数字 (v),再通过回流的 ((c,c')) 做到 (-1)(在可行流里,就会体现为一个 (c ightarrow c' ightarrow c) 的回路),(+1) 比较简单,不在赘述。

      剩下的就简单啦,所有可修改的行限制、列限制、空格子数字都可以用这种边的组合体现,建出图后跑有源汇上下界最小费用可行流即可。

      最小费用可行流就是把计算可行流时,求最大流的算法换成求最小费用最大流的算法,由于如此建图不存在正向负权边,所以正确性保证。

    (mathcal{Code})

    /* Clearink */
    
    #include <queue>
    #include <cstdio>
    #include <cassert>
    
    #define int long long
    typedef std::pair<int, int> pii;
    
    const int MAXN = 100, INF = 0x3f3f3f3f;
    int n, m, type[MAXN + 5][MAXN + 5], cnt, id[MAXN + 5][MAXN + 5];
    int deg[MAXN * MAXN * 2 + 10];
    pii num[MAXN + 5][MAXN + 5], cost[MAXN + 5][MAXN + 5];
    
    inline int imin ( const int a, const int b ) { return a < b ? a : b; }
    
    struct MaxFlowCostGraph {
    	static const int MAXND = MAXN * MAXN * 4 + 4, MAXEG = MAXN * MAXN * 100;
    	int ecnt, head[MAXND + 5], S, T, bound, curh[MAXND + 5], d[MAXND + 5];
    	bool inq[MAXND + 5];
    	struct Edge { int to, flw, cst, nxt; } graph[MAXEG * 2 + 5];
    
    	MaxFlowCostGraph (): ecnt ( 1 ) {}
    
    	inline void link ( const int s, const int t, const int f, const int w ) {
    		graph[++ecnt] = { t, f, w, head[s] };
    		head[s] = ecnt;
    	}
    
    	inline Edge& operator [] ( const int k ) { return graph[k]; }
    
    	inline void operator () ( int s, int t, const int f, const int w ) {
    		#ifdef RYBY
    			printf ( "%lld %lld ", s, t );
    			if ( f == INF ) printf ( "INF " );
    			else printf ( "%lld ", f );
    			printf ( "%lld
    ", w );
    		#endif
    		link ( s, t, f, w ), link ( t, s, 0, -w );
    	}
    
    	inline bool spfa () {
    		static std::queue<int> que;
    		for ( int i = 0; i <= bound; ++i ) d[i] = INF, inq[i] = false;
    		d[S] = 0, inq[S] = true, que.push ( S );
    		while ( !que.empty () ) {
    			int u = que.front (); que.pop ();
    			inq[u] = false;
    			for ( int i = head[u], v; i; i = graph[i].nxt ) {
    				if ( graph[i].flw && d[v = graph[i].to] > d[u] + graph[i].cst ) {
    					d[v] = d[u] + graph[i].cst;
    					if ( !inq[v] ) que.push ( v ), inq[v] = true;
    				}
    			}
    		}
    		return d[T] != INF;
    	}
    
    	inline pii dfs ( const int u, const int iflw ) {
    		if ( u == T ) return { iflw, 0 };
    		inq[u] = true; pii ret ( 0, 0 );
    		for ( int& i = curh[u], v; i; i = graph[i].nxt ) {
    			if ( graph[i].flw && !inq[v = graph[i].to]
    			&& d[v] == d[u] + graph[i].cst ) {
    				pii oflw ( dfs ( v, imin ( iflw - ret.first, graph[i].flw ) ) );
    				graph[i].flw -= oflw.first, graph[i ^ 1].flw += oflw.first;
    				ret.first += oflw.first;
    				ret.second += graph[i].cst * oflw.first + oflw.second;
    				if ( ret.first == iflw ) break;
    			}
    		}
    		if ( !ret.first ) d[u] = INF;
    		return inq[u] = false, ret;
    	}
    
    	inline pii calc ( const int s, const int t ) {
    		S = s, T = t;
    		pii ret ( 0, 0 );
    		while ( spfa () ) {
    			for ( int i = 0; i <= bound; ++i ) inq[i] = false, curh[i] = head[i];
    			pii tmp ( dfs ( S, INF ) );
    			ret.first += tmp.first, ret.second += tmp.second;
    		}
    		return ret;
    	}
    } graph;
    
    inline void readKakuro ( pii arr[MAXN + 5][MAXN + 5] ) {
    	for ( int i = 1; i <= n; ++i ) {
    		for ( int j = 1; j <= m; ++j ) {
    			arr[i][j].first = arr[i][j].second = -1;
    			if ( type[i][j] == 1 || type[i][j] == 4 ) {
    				scanf ( "%lld", &arr[i][j].first );
    			} else if ( type[i][j] == 2 ) {
    				scanf ( "%lld", &arr[i][j].second );
    			} else if ( type[i][j] == 3 ) {
    				scanf ( "%lld %lld", &arr[i][j].first, &arr[i][j].second );
    			}
    		}
    	}
    }
    
    inline int ident ( const int i, const int j, const bool r, const bool t = false ) {
    	assert ( 1 <= i && i <= n );
    	assert ( 1 <= j && j <= m );
    	assert ( type[i][j] && ( !t || ( t && ( type[i][j] == 2 || type[i][j] == 3 ) ) ) );
    	return r * cnt + id[i][j] + t;
    }
    
    inline void specLink ( const int s, const int t, const int f, const int c ) {
    	if ( ~c ) {
    		// (s,t,[1,f],-c) and (s,t,[0,INF],c).
    		graph ( s, t, INF, c );
    		graph ( t, s, f - 1, c ), deg[s] -= f, deg[t] += f;
    	} else {
    		deg[s] -= f, deg[t] += f;
    	}
    }
    
    signed main () {
    	scanf ( "%lld %lld", &n, &m );
    	for ( int i = 1; i <= n; ++i ) {
    		for ( int j = 1; j <= m; ++j ) {
    			scanf ( "%lld", &type[i][j] );
    			if ( type[i][j] ) id[i][j] = ++cnt, cnt += type[i][j] < 4;
    		}
    	}
    	int rS = cnt << 1 | 1, rT = rS + 1;
    	int vS = rT + 1, vT = graph.bound = vS + 1;
    	#ifdef RYBY
    		printf ( "(%lld,%lld) & (%lld,%lld)
    ", rS, rT, vS, vT );
    	#endif
    	readKakuro ( num ), readKakuro ( cost );
    	for ( int i = 1; i <= n; ++i ) {
    		for ( int j = 1; j <= m; ++j ) {
    			if ( !type[i][j] ) continue;
    			if ( type[i][j] == 4 ) {
    				specLink ( ident ( i, j, 0 ), ident ( i, j, 1 ),
    					num[i][j].first, cost[i][j].first );
    				continue;
    			}
    			if ( ~num[i][j].first ) {
    				int cur = ident ( i, j, 0, 0 );
    				specLink ( rS, cur, num[i][j].first, cost[i][j].first );
    				for ( int k = i + 1; type[k][j] == 4; ++k ) {
    					graph ( cur, ident ( k, j, 0 ), INF, 0 );
    				}
    			}
    			if ( ~num[i][j].second ) {
    				int cur = ident ( i, j, 1, 1 );
    				specLink ( cur, rT, num[i][j].second, cost[i][j].second );
    				for ( int k = j + 1; type[i][k] == 4; ++k ) {
    					graph ( ident ( i, k, 1 ), cur, INF, 0 );
    				}
    			}
    		}
    	}
    	int req = 0;
    	#ifdef RYBY
    		puts ( "balancing degree..." );
    	#endif
    	for ( int i = 1; i <= rT; ++i ) {
    		if ( deg[i] > 0 ) graph ( vS, i, deg[i], 0 );
    		else if ( deg[i] ) req -= deg[i], graph ( i, vT, -deg[i], 0 );
    	}
    	graph ( rT, rS, INF, 0 );
    	pii res ( graph.calc ( vS, vT ) );
    	#ifdef RYBY
    		printf ( "req = %lld;
    res = %lld %lld.
    ", req, res.first, res.second );
    	#endif
    	if ( res.first != req ) puts ( "-1" );
    	else printf ( "%lld
    ", res.second );
    	return 0;
    }
    
    
  • 相关阅读:
    datatime,time,string转换和format格式化处理
    迭代器和生成器进一步的认识
    对数据结构的新认识
    python StringIO中的 read()与 getvalue()
    git 分支策略
    js词法分析
    js作用域
    注解
    MapReduce过程详解(基于hadoop2.x架构)
    指数基金
  • 原文地址:https://www.cnblogs.com/rainybunny/p/14190971.html
Copyright © 2011-2022 走看看