[NOI2010]海拔

题目描述

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想知道在最理想的情况下(即你可以任意假设其他路口的海拔高度),每天上班高峰期间所有人爬坡消耗的总体力和的最小值。

输入输出格式

输入格式:

第一行包含一个整数n,含义如上文所示。

接下来4n(n + 1)行,每行包含一个非负整数分别表示每一条道路每一个方向的人流量信息。输入顺序:n(n + 1)个数表示所有从西到东方向的人流量,然后n(n + 1)个数表示所有从北到南方向的人流量,n(n + 1)个数表示所有从东到西方向的人流量,最后是n(n + 1)个数表示所有从南到北方向的人流量。对于每一个方向,输入顺序按照起点由北向南,若南北方向相同时由西到东的顺序给出(参见样例输入)。

输出格式:

仅包含一个数,表示在最理想情况下每天上班高峰期间所有人爬坡所消耗的总体力和(即总体力和的最小值),结果四舍五入到整数。

输入输出样例

输入样例#1:

1
1
2
3
4
5
6
7
8

输出样例#1:

3

说明

对于20%的数据:n ≤ 3;

对于50%的数据:n ≤ 15;

对于80%的数据:n ≤ 40;

对于100%的数据:1 ≤ n ≤ 500,0 ≤ 流量 ≤ 1,000,000且所有流量均为整数。


题解

平面图转对偶图

首先给出平面图的定义:

平面图就是边与边不相交的连通图

所以网格图就是最典型的平面图

然后平面图怎么转对偶图呢?

就是把边围成的面看做点

然后再把\(S->T\)分开

一边是S面,一边是T面

然后每条边所相邻的两个面所代表的点连一条边权为这条边的边权的边

这样就建出了对偶图

一条从\(S->T\)的路径就代表了一个\(S->T\)的割

这样求平面图的最小割问题我们就可以转换为平面图跑最短路\(O(nlog_n)\)解决

这道题就是一道平面图转对偶图的裸题

因为左上角是0,右下角是1,所以中间的高度一定在\(0~1\)之间

如果一个点的高度不是0或1,那么这个点改为0或1一定更优

所以就可以直接把平面图转对偶图,从左往右的边就可以从下面往上面连边,从上往下的边就可以从左面往右面连边

然后跑最短路就好了

题解

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int N = 505 ;
const int M = 300005 ;
using namespace std ;
inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

bool vis[M] ;
int n , S , T , num , hea[M] , Flow1[N][N] , Flow2[N][N] ;
LL dis[M] ;
struct Node { int id , dis ; } ;
struct E { int Nxt , to , dis ; } edge[M * 10] ;
inline void add_edge(int from , int to , int dis) {
    edge[++num].Nxt = hea[from] ; edge[num].to = to ;
    edge[num].dis = dis ; hea[from] = num ;
}
inline void Rev_add_edge(int from , int to , int dis) { add_edge(to , from , dis) ; }
inline void Insert(int u , int v , int w) {
    add_edge(u , v , w) ; 
    add_edge(v , u , w) ;
}
inline bool operator < (Node a , Node b) {
    return a.dis > b.dis ;
}
inline int Dijkstra() {
    memset(dis , 63 , sizeof(dis)) ; dis[S] = 0 ;
    priority_queue < Node > q ; q.push((Node) { S , 0 }) ;
    while(!q.empty()) {
        int u = q.top().id ; q.pop() ; 
        if(vis[u]) continue ; vis[u] = true ;
        for(int i = hea[u] ; i ; i = edge[i].Nxt) {
            int v = edge[i].to ;
            if(dis[v] > dis[u] + edge[i].dis) {
                dis[v] = dis[u] + edge[i].dis ;
                if(!vis[v]) q.push((Node) { v , dis[v] }) ;
            }
        }
    }
    return dis[T] ;
}
int main() {
    n = read() ; S = 0 , T = 300002 ;
    for(int i = 1 , w ; i <= n + 1 ; i ++)
        for(int j = 1 ; j <= n ; j ++) {
            w = read() ;
            if(i == 1) add_edge(j , T , w) ;
            else if(i == n + 1) add_edge(S , (i - 2) * n + j , w) ;
            else add_edge((i - 1) * n + j , (i - 2) * n + j , w) ;
        }
    for(int i = 1 , w ; i <= n ; i ++)
        for(int j = 1 ; j <= n + 1 ; j ++) {
            w = read() ;
            if(j == 1) add_edge(S , (i - 1) * n + j , w) ;
            else if(j == n + 1) add_edge((i - 1) * n + j - 1 , T , w) ;
            else add_edge((i - 1) * n + j - 1 , (i - 1) * n + j , w) ;
        }
    for(int i = 1 , w ; i <= n + 1 ; i ++)
        for(int j = 1 ; j <= n ; j ++) {
            w = read() ;
            if(i == 1) Rev_add_edge(j , T , w) ;
            else if(i == n + 1) Rev_add_edge(S , (i - 2) * n + j , w) ;
            else Rev_add_edge((i - 1) * n + j , (i - 2) * n + j , w) ;
        }
    for(int i = 1 , w ; i <= n ; i ++)
        for(int j = 1 ; j <= n + 1 ; j ++) {
            w = read() ;
            if(j == 1) Rev_add_edge(S , (i - 1) * n + j , w) ;
            else if(j == n + 1) Rev_add_edge((i - 1) * n + j - 1 , T , w) ;
            else Rev_add_edge((i - 1) * n + j - 1 , (i - 1) * n + j , w) ;
        }
    cout << Dijkstra() << endl ;
    return 0 ;
}

猜你喜欢

转载自www.cnblogs.com/beretty/p/10098624.html
今日推荐