【BZOJ-2007】海拔 最小割 (平面图转对偶图 + 最短路)

2007: [Noi2010]海拔

Time Limit: 20 Sec  Memory Limit: 552 MB
Submit: 2095  Solved: 1002
[Submit][Status][Discuss]

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
【样例说明】
样例数据见下图。
最理想情况下所有点的海拔如上图所示。
【数据规模】
对于20%的数据:n ≤ 3;
对于50%的数据:n ≤ 15;
对于80%的数据:n ≤ 40;
对于100%的数据:1 ≤ n ≤ 500,0 ≤ 流量 ≤ 1,000,000且所有流量均为整数。

HINT

Source

Solution

典型的平面图,所以肯定利用其性质

很BZOJ1001狼爪兔子很像,直接平面图转对偶图求最短路即为最小割,那么就比较简单了

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define maxn 250010
int n;
struct EdgeNode{int next,to,len;}edge[maxn*10];
int head[maxn],cnt;
void add(int u,int v,int w) {cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].len=w;}
void insert(int u,int v,int w) {add(u,v,w);}
bool visit[maxn]; int dis[maxn],S,T;
#define inf 0x7fffffff
void spfa()
{
    queue<int>q;
    for (int i=S; i<=T; i++) dis[i]=inf;
    q.push(S); visit[S]=1; dis[S]=0;
    while (!q.empty())
        {
            int now=q.front(); q.pop(); visit[now]=0;
            for (int i=head[now]; i; i=edge[i].next)
                if (dis[edge[i].to]>dis[now]+edge[i].len)
                    {
                        dis[edge[i].to]=dis[now]+edge[i].len;
                        if (!visit[edge[i].to])
                            visit[edge[i].to]=1,q.push(edge[i].to);
                    }
        }
}
int loc(int x,int y) {return (x-1)*n+y;}
int main()
{
    n=read(); S=0,T=n*n+1;
    for (int i=1; i<=n+1; i++)
        for (int x,j=1; j<=n; j++)
            {
                x=read();
                if (i==1) insert(loc(i,j),T,x); else if (i==n+1) insert(S,loc(i-1,j),x); else insert(loc(i,j),loc(i-1,j),x);
            }
    for (int i=1; i<=n; i++)
        for (int x,j=1; j<=n+1; j++)
            {
                x=read();
                if (j==1) insert(S,loc(i,j),x); else if (j==n+1) insert(loc(i,j-1),T,x); else insert(loc(i,j-1),loc(i,j),x);
            }
    for (int i=1; i<=n+1; i++)
        for (int x,j=1; j<=n; j++)
            {
                x=read();
                if (i==1) insert(T,loc(i,j),x); else if (i==n+1) insert(loc(i-1,j),S,x); else insert(loc(i-1,j),loc(i,j),x);
            }
    for (int i=1; i<=n; i++)
        for (int x,j=1; j<=n+1; j++)
            {
                x=read();
                if (j==1) insert(loc(i,j),S,x); else if (j==n+1) insert(T,loc(i,j-1),x); else insert(loc(i,j),loc(i,j-1),x);
            }
    spfa();
    printf("%d\n",dis[T]);
    return 0;
}

写spfa的sb选手...跑出dijkstra的10倍时间了....

posted @ 2016-05-22 08:20  DaD3zZ  阅读(276)  评论(0编辑  收藏  举报