BZOJ2007: [Noi2010]海拔

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

Sample Output

3

【样例说明】
样例数据见下图。

最理想情况下所有点的海拔如上图所示。
对于100%的数据:1 ≤ n ≤ 500,0 ≤ 流量 ≤ 1,000,000且所有流量均为整数。

题解Here!

一眼望去——最小割嘛。。。
然后跑最大流$Dinic$不就好了嘛。。。
$3min$后——
啥玩意?$TLE$???
一脸黑人问号。。。
回头算了下数据——$2.5\times 10^5$。。。
这好像真的是要$T$的节奏啊。。。
想一想,除了跑网络流还有什么办法。。。
等一下,平面网格图!
转成对偶图再跑个$spfa$不久完了嘛???
$5min$后——
欸竟然过了。。。
我很想知道这题我是怎么用$Dinic$水过去的:BZOJ1001: [BeiJing2006]狼抓兔子
然后这个题怎么转成对偶图很麻烦。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#define MAXN 260010
#define MAX (1LL<<62)
using namespace std;
int n,s,t,c=1;
int head[MAXN];
long long path[MAXN];
bool vis[MAXN];
struct Edge{
    int next,to;
    long long w;
}a[MAXN<<3];
inline int read(){
    int date=0;char c=0;
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    return date;
}
inline int get_id(int x,int y){return (x-1)*n+y;}
inline int relax(int u,int v,int w){
    if(path[v]>path[u]+w){
        path[v]=path[u]+w;
        return 1;
    }
    return 0;
}
inline void add(int u,int v,int w){
    a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++;
}
void spfa(){
    int u,v;
    queue<int> q;
    for(int i=1;i<=t;i++){path[i]=MAX;vis[i]=false;}
    path[s]=0;
    vis[s]=true;
    q.push(s);
    while(!q.empty()){
        u=q.front();
        q.pop();
        vis[u]=false;
        for(int i=head[u];i;i=a[i].next){
            v=a[i].to;
            if(relax(u,v,a[i].w)&&!vis[v]){
                vis[v]=true;
                q.push(v);
            }
        }
    }
    printf("%lld\n",path[t]);
}
void init(){
    int u,v,w;
    n=read();
    s=n*n+1;t=n*n+2;
    for(int i=1;i<=n+1;i++)
    for(int j=1;j<=n;j++){
        w=read();
        u=get_id(i,j);
        if(i==1)add(u,t,w);
        else if(i==n+1)add(s,u-n,w);
        else add(u,u-n,w);
    }
    for(int i=1;i<=n;i++)
    for(int j=0;j<=n;j++){
        w=read();
        u=get_id(i,j);
        if(j==0)add(s,u+1,w);
        else if(j==n)add(i*n,t,w);
        else add(u,u+1,w);
    }
    for(int i=1;i<=n+1;i++)
    for(int j=1;j<=n;j++){
        w=read();
        u=get_id(i,j);
        if(i==1)add(t,u,w);
        else if(i==n+1)add(u-n,s,w);
        else add(u-n,u,w);
    }
    for(int i=1;i<=n;i++)
    for(int j=0;j<=n;j++){
        w=read();
        u=get_id(i,j);
        if(j==0)add(u+1,s,w);
        else if(j==n)add(t,u-1,w);
        else add(u+1,u,w);
    }
}
int main(){
    init();
    spfa();
    return 0;
}

 

posted @ 2018-10-11 23:30  符拉迪沃斯托克  阅读(307)  评论(0编辑  收藏  举报
Live2D