• @bzoj



    @description@

    一个长 P 宽 Q 高 R 的长方体点阵,每个点 (x, y, z) 有一个权值 v(x, y, z)。
    请找到一个函数 f(x, y) 使得 1 ≤ f(x, y) ≤ R 且对于 |x - x'| + |y - y'| = 1,必须有 |f(x, y) - f(x', y')| ≤ D。
    最小化 ∑v(x, y, f(x, y))。

    Input
    第一行是三个正整数P,Q,R,表示切糕的长P、 宽Q、高R。
    第二行有一个非负整数D,表示光滑性要求。接下来是R个P行Q列的矩阵,第z个矩阵的第x行第y列是v(x,y,z) (1≤x≤P, 1≤y≤Q, 1≤z≤R)。
    100%的数据满足P,Q,R≤40,0≤D≤R,且给出的所有的不和谐值不超过1000。

    Output
    仅包含一个整数,表示在合法基础上最小的总不和谐值。

    Sample Input
    2 2 2
    1
    6 1
    6 1
    2 6
    2 6
    Sample Output
    6

    @solution@

    经典中的经典.jpg。

    考虑 |f(x, y) - f(x', y')| ≤ D 可以拆成 f(x, y) ≤ f(x', y') + D 与 f(x', y') ≤ f(x, y) + D。
    于是我们只需要保证一对 (x, y) 相邻的 f 值 ≤ 它本身的 f 值 + D 即可。
    考虑上式,其实相当于规定一些点不能够共存,这使我们联想到最小割。

    我们对于每一对 (x, y) 建 R 个点串成一条链,源点连链头,汇点连链尾。在这条链上割掉 (a, b) 这条边相当于令 f(x, y) = b(a 可以为源点),所以我们令 (a, b) 的容量为 v(x, y, b),当 b 为汇点时容量为 inf。

    考虑怎么用 inf 边表示我们的矛盾与共存关系。
    依上所述,假如 (x', y') 与 (x, y) 相邻,对于 (x, y) 中的第 i 个点,我们可以从 (x', y') 中的第 i + D 个点出发连一条 inf 边向 (x, y) 中的第 i 个点。
    这样的话,假如我们选中了第 i 个点(相当于割掉第 i 个点之前的边),则会出现路径 s -> (x', y') 的 i + D -> (x, y) 的 i -> t。
    因为选中 “(x, y) 的 i -> t” 这一段路径割肯定会不优(每条路径只割一条边是最优的),所以我们必然会割 “s -> (x', y') 的 i + D” 这一段,而这就完成了我们的目的。

    @accepted code@

    #include<cstdio>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int MAXN = 45;
    const int MAXV = MAXN*MAXN*MAXN;
    const int MAXE = 30*MAXV;
    const int INF = (1<<30);
    const int dx[] = {0, 0, 1, -1};
    const int dy[] = {1, -1, 0, 0};
    struct FlowGraph{
    	struct edge{
    		int to, cap, flow;
    		edge *nxt, *rev;
    	}edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;
    	FlowGraph() {ecnt = &edges[0];}
    	void addedge(int u, int v, int c) {
    		edge *p = (++ecnt), *q = (++ecnt);
    		p->to = v, p->cap = c, p->flow = 0;
    		p->nxt = adj[u], adj[u] = p;
    		q->to = u, q->cap = 0, q->flow = 0;
    		q->nxt = adj[v], adj[v] = q;
    		p->rev = q, q->rev = p;
    	}
    	int d[MAXV + 5], s, t;
    	queue<int>que;
    	bool relabel() {
    		for(int i=s;i<=t;i++)
    			d[i] = INF, cur[i] = adj[i];
    		d[t] = 0; que.push(t);
    		while( !que.empty() ) {
    			int f = que.front(); que.pop();
    			for(edge *p=adj[f];p;p=p->nxt)
    				if( p->rev->cap > p->rev->flow )
    					if( d[f] + 1 < d[p->to] ) {
    						d[p->to] = d[f] + 1;
    						que.push(p->to);
    					}
    		}
    		return !(d[s] == INF);
    	}
    	int aug(int x, int tot) {
    		if( x == t ) return tot;
    		int sum = 0;
    		for(edge *&p=cur[x];p;p=p->nxt) {
    			if( p->cap > p->flow && d[p->to] + 1 == d[x] ) {
    				int del = aug(p->to, min(tot - sum, p->cap - p->flow));
    				p->flow += del, p->rev->flow -= del, sum += del;
    				if( sum == tot ) return sum;
    			}
    		}
    		return sum;
    	}
    	int max_flow(int _s, int _t) {
    		s = _s, t = _t; int flow = 0;
    		while( relabel() )
    			flow += aug(s, INF);
    		return flow;
    	}
    }G;
    int P, Q, R, D, s, t;
    int id[MAXN][MAXN][MAXN], cnt;
    int v[MAXN][MAXN][MAXN];
    int main() {
    	scanf("%d%d%d%d", &P, &Q, &R, &D);
    	for(int i=1;i<=R;i++)
    		for(int j=1;j<=P;j++)
    			for(int k=1;k<=Q;k++)
    				scanf("%d", &v[i][j][k]), id[i][j][k] = (++cnt);
    	s = 0, t = cnt + 1;
    	for(int i=1;i<=R;i++)
    		for(int j=1;j<=P;j++)
    			for(int k=1;k<=Q;k++)
    				G.addedge(id[i-1][j][k], id[i][j][k], v[i][j][k]);
    	for(int j=1;j<=P;j++)
    		for(int k=1;k<=Q;k++)
    			G.addedge(id[R][j][k], t, INF);
    	for(int i=1;i+D<=R;i++)
    		for(int j=1;j<=P;j++)
    			for(int k=1;k<=Q;k++)
    				for(int l=0;l<4;l++) {
    					int x = j + dx[l], y = k + dy[l];
    					if( x < 1 || y < 1 || x > P || y > Q ) continue;
    					G.addedge(id[i+D][x][y], id[i][j][k], INF);
    				}
    	printf("%d
    ", G.max_flow(s, t));
    }
    

    @details@

    同理,假如每个点有若干个连续的状态 1...p[i],某些点对之间会有限制(比如要求两个点选择的状态编号的差的绝对值不超过 k),则可以采用类似上文的建模方法。

    据说这个模型叫作切糕模型。

  • 相关阅读:
    iOS开发日记37-app加急审核
    iOS开发日记36-iOS中的依赖注入
    iOS开发日记35-事件处理机制与图像渲染过程
    iOS开发日记34-国际化
    iOS开发日记33-hit-Test
    iOS开发日记32-详解多线程(死锁)
    iOS开发日记31-详解Block
    iOS开发日记30-Autolayout
    springMVC js等文件找不到解决方法
    jQuery UI 中Tabs Ajax载入时出现Http 304的问题
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11396390.html
Copyright © 2020-2023  润新知