bzoj2007 [Noi2010]海拔
Description
YT市是一个规划良好的城市,城市被东西向和南北向的主干道划分为n×n个区域。简单起见,可以将YT市看作 一个正方形,每一个区域也可看作一个正方形。从而,YT城市中包括(n+1)×(n+1)个交叉路口和2n×(n+1)条双向道路(简称道路),每条双向 道路连接主干道上两个相邻的交叉路口。下图为一张YT市的地图(n = 2),城市被划分为2×2个区域,包括3×3个交叉路口和12条双向道路。
小Z作为该市的市长,他根据统计信息得到了每天上班高峰期间YT市每条道路两个方向的人流量,即在高峰期间沿 着该方向通过这条道路的人数。每一个交叉路口都有不同的海拔高度值,YT市市民认为爬坡是一件非常累的事情,每向上爬h的高度,就需要消耗h的体力。如果 是下坡的话,则不需要耗费体力。因此如果一段道路的终点海拔减去起点海拔的值为h(注意h可能是负数),那么一个人经过这段路所消耗的体力是max{0, h}(这里max{a, b}表示取a, b两个值中的较大值)。
小Z还测量得到这个城市西北角的交叉路口海拔为0,东南角的交叉路口海拔为1(如上图所示),但其它交叉路口的海拔高度都无法得知。小Z想知道在最理想的情况下(即你可以任意假设其他路口的海拔高度),每天上班高峰期间所有人爬坡消耗的总体力和的最小值。
Input
第一行包含一个整数n,含义如上文所示。接下来4n(n + 1)行,每行包含一个非负整数分别表示每一条道路每一个
方向的人流量信息。输入顺序:n(n + 1)个数表示所有从西到东方向的人流量,然后n(n + 1)个数表示所有从北到
南方向的人流量,n(n + 1)个数表示所有从东到西方向的人流量,最后是n(n + 1)个数表示所有从南到北方向的人
流量。对于每一个方向,输入顺序按照起点由北向南,若南北方向相同时由西到东的顺序给出(参见样例输入)。
Output
仅包含一个数,表示在最理想情况下每天上班高峰期间所有人爬坡所消耗的总体力和(即总体力和的最小值),结
果四舍五入到整数。
Sample Input
1
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
Sample Output
3
【样例说明】
样例数据见下图。
最理想情况下所有点的海拔如上图所示。
对于100%的数据:1 ≤ n ≤ 500,0 ≤ 流量 ≤ 1,000,000且所有流量均为整数。
分析:不算太难的一道题.
先大胆猜一个结论:所有点的海拔要么是1,要么是0,并且海拔为0的连成一个连通块, 海拔为1的连成一个连通块.
第一个小结论是很显然的. 如果有点的海拔超过了1,把它设置为1一定更优.
对于第二个小结论,我们可以单独分析一条路径:000101111. 如果0,1不连续,那么最右边的0与左边的1就会产生代价.如果变成:000111111,就不会产生这个代价了.
现在的问题就变成了怎么求两个连通块的分界线. 分界线使得原图变成两个连通块,并且左上角的点不能到达右下角的点.
分界线实际上就是最小割. 因为每条边0 ---> 1都会产生代价,要使得左上角的点与右下角的点不连通,就是最小割啦. 怎么求最小割?
这是一个平面图,平面图求最小割的方法是转换成对偶图求最短路,bzoj1001有无向图的做法. 有向图怎么办呢?
对于原图中的每一条有向边,在对偶图中将它逆时针旋转90°,将两个平面连接起来就好了.
最后要注意的一点:图比较稠密,要用dijkstra!
#include <cstdio> #include <queue> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const ll maxn = 510 * 510; ll n,S,T,head[maxn],to[maxn * 4],nextt[maxn * 4],w[maxn * 4],tot = 1,d[maxn]; ll vis[maxn]; struct node { ll len,x; }; bool operator < (node a,node b) { return a.len > b.len; } void add(ll x,ll y,ll z) { w[tot] = z; to[tot] = y; nextt[tot] = head[x]; head[x] = tot++; } void dijkstra() { memset(d,127/3,sizeof(d)); d[S] = 0; priority_queue<node> q; node t; t.len = 0; t.x = S; q.push(t); while (!q.empty()) { while (!q.empty() && vis[q.top().x]) q.pop(); if(q.empty()) break; node u = q.top(); q.pop(); ll len = u.len,x = u.x; vis[x] = 1; for (ll i = head[x]; i; i = nextt[i]) { ll v = to[i]; if (d[v] > len + w[i]) { d[v] = len + w[i]; node temp; temp.x = v; temp.len = d[v]; q.push(temp); } } } } void pre() { n++; for (ll i = 1; i <= n; i++) for (ll j = 1; j < n; j++) { ll x; scanf("%lld",&x); ll temp = (i - 1) * (n - 1) + j; if (i == 1) add(temp,T,x); else if (i == n) { temp -= (n - 1); add(S,temp,x); } else add(temp,temp - n + 1,x); } for (ll i = 1; i < n; i++) for (ll j = 1; j <= n; j++) { ll x; scanf("%lld",&x); ll temp = (i - 1) * (n - 1) + j; if (j == 1) add(S,temp,x); else if (j == n) { temp--; add(temp,T,x); } else add(temp - 1,temp,x); } for (ll i = 1; i <= n; i++) for (ll j = 1; j < n; j++) { ll x; scanf("%lld",&x); ll temp = (i - 1) * (n - 1) + j; if (i == 1) add(T,temp,x); else if (i == n) { temp -= (n - 1); add(temp,S,x); } else add(temp - n + 1,temp,x); } for (ll i = 1; i < n; i++) for (ll j = 1; j <= n; j++) { ll x; scanf("%lld",&x); ll temp = (i - 1) * (n - 1) + j; if (j == 1) add(temp,S,x); else if (j == n) { temp--; add(T,temp,x); } else add(temp,temp - 1,x); } } int main() { scanf("%lld",&n); S = n * n + 1; T = S + 1; pre(); dijkstra(); printf("%lld\n",d[T]); return 0; }