1. 程式人生 > 實用技巧 >Solution -「BJWC 2018」「洛谷 P4486」Kakuro

Solution -「BJWC 2018」「洛谷 P4486」Kakuro

\(\mathcal{Description}\)

  Link.

  有一個\(n\times m\) 的網格圖,其中某些格子被主對角線劃成兩個三角形,稱這樣的格子為特殊格;初始時,除了一些障礙格,所有空格子和特殊格的兩個三角形內都分別填上了數字。稱一個網格合法,當且僅當:

  • 對於每個特殊格左下方的三角形,若其不是障礙格,則其下方連續的空格子內數字之和為三角形內數字;
  • 對於每個特殊格右上方的三角形,若其不是障礙格,則其右方連續的空格子內數字之和為三角形內數字。

  為了使網格合法,你可以以一定代價將某個格子內的數 \(+1\)\(-1\),修改每個格子的代價是獨立的,亦有一些格子不能修改。

  求最小代價。

  \(n,m\le30\),每個空格子至少在一個左下非障礙的特殊格子下方,或在一個右上非障礙的特殊格子右方。

  我幹嘛還要再概括一遍那麼長的題意 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\rightarrow c'\rightarrow 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\n", 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)\n", 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;\nres = %lld %lld.\n", req, res.first, res.second );
	#endif
	if ( res.first != req ) puts ( "-1" );
	else printf ( "%lld\n", res.second );
	return 0;
}