【网络流】【对偶图】【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 Format 	
第一行包含一个整数n,含义如上文所示。接下来4n(n + 1)行,每行包含一个非负整数分别表示每一条道路每一个方向的人流量信息。输入顺序:n(n + 1)个数表示所有从西到东方向的人流量,然后n(n + 1)个数表示所有从北到南方向的人流量,n(n + 1)个数表示所有从东到西方向的人流量,最后是n(n + 1)个数表示所有从南到北方向的人流量。对于每一个方向,输入顺序按照起点由北向南,若南北方向相同时由西到东的顺序给出(参见样例输入)。

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

样例输入 Sample Input
1
1
2
3
4
5
6
7
8	

样例输出 Sample Output
3

时间限制 Time Limitation 	
各个测试点2S

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

::点击图片在新窗口中打开::

最理想情况下所有点的海拔如上图所示。
【数据规模】
对于20%的数据:n ≤ 3;
对于50%的数据:n ≤ 15;
对于80%的数据:n ≤ 40;
对于100%的数据:1 ≤ n ≤ 500,0 ≤ 流量 ≤ 1,000,000且所有流量均为整数。
【提示】
海拔高度不一定是整数。
朴素算法是从左上角到右下角求一次最大流,求得的这个最大流即为最小割。但这样最多只能得80分(我的程序比较弱,只能过70分)。

于是可以用对偶图优化。
首先可以断定,海拔必为0或1中的一种(不然可以转化为只有0和1的情况),那么可以找到一条0和1的分界线。

我们将平面图上所有的边全都沿中点逆时针旋转90°,将原来每个单位网格的中心点变为对偶图的点,(自然有些边就指向边界外面或是从边界外指向某个点了。)再把对偶图的整个左下区域增加一个超级源,整个右上区域增加一个超级汇,从超级源到超级汇求一次最短路即可。


70分程序:

#include <cstdio>
#include <string>
#include <cstdlib>
#include <algorithm>

using std::min;
const char fi[] = "altitude.in";
const char fo[] = "altitude.out";
const int maxN = 30010;
const int MAX = 0x3f3f3f3f;
const int MIN = ~MAX;

struct Edge
{
    int u, v, f;
    Edge *next, *back;
};

Edge *edge[maxN];
int d[maxN];
int cnt[maxN];
int N, n, S, T;

void init_file()
{
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
    return;
}

inline int getint()
{
    int res = 0;
    char tmp;
    while (!isdigit(tmp = getchar()));
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return res;
}

inline void insert(int u, int v, int f)
{
    Edge *p = new Edge;
    p -> v = v;
    p -> f = f;
    p -> next = edge[u];
    edge[u] = p;
    p = new Edge;
    p -> v = u;
    p -> f = 0;
    p -> next = edge[v];
    edge[v] = p;
    edge[u] -> back = edge[v];
    edge[v] -> back = edge[u];
    return;
}

void readdata()
{
    ++(N = getint());
    S = 1;
    T = n = N * N;
    for (int i = 0; i < N; ++i)
    for (int j = 1; j < N; ++j)
        insert(i * N + j,
               i * N + j + 1,
               getint());
    for (int i = 0; i < N - 1; ++i)
    for (int j = 1; j < N + 1; ++j)
        insert(i * N + j,
               (i + 1) * N + j,
               getint());
    for (int i = 0; i < N; ++i)
    for (int j = 1; j < N; ++j)
        insert(i * N + j + 1,
               i * N + j, getint());
    for (int i = 0; i < N - 1; ++i)
    for (int j = 1; j < N + 1; ++j)
        insert((i + 1) * N + j,
               i * N + j, getint());
    return;
}

int Sap(int u, int Lim)
{
    if (u == T) return Lim;
    int tmp = 0;
    for (Edge *p = edge[u]; p; p = p -> next)
    if (p -> f > 0 && d[u] == d[p -> v] + 1)
    {
        int k = Sap(p -> v, min(Lim - tmp, p -> f));
        p -> f -= k;
        p -> back -> f += k;
        if ((tmp += k) == Lim) return tmp;
    }
    if (d[S] >= n) return tmp;
    if (!(--cnt[d[u]])) d[S] = n;
    ++cnt[++d[u]];
    return tmp;
}

void work()
{
    int ans = 0;
    cnt[0] = n;
    while (d[S] < n) ans += Sap(S, MAX);
    printf("%d\n", ans);
    return;
}

int main()
{
    init_file();
    readdata();
    work();
    return 0;
}
Accode:(手写堆版)

#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>

using namespace std;
const char fi[] = "altitude.in";
const char fo[] = "altitude.out";
const int maxN = 250010; //500 * 500 = 250,000!!

typedef long long int64;
struct Edge
{
    int u, v;
    int64 d;
    Edge *next;
};

struct Node
{
    int ID;
    int64 d;
    Node() {}
    Node(int ID, int64 d): ID(ID), d(d) {}
};

Edge *edge[maxN];
Node hp[maxN];
int64 dist[maxN];
bool marked[maxN];
int N, n, S, T, top;

void init_file()
{
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
    return;
}

inline int getint()
{
    int res = 0; char tmp;
    while (!isdigit(tmp = getchar()));
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return res;
}

inline void insert(int u, int v, int64 d)
{
    Edge *p = new Edge;
    p -> v = v;
    p -> d = d;
    p -> next = edge[u];
    edge[u] = p;
    return;
}

inline void readdata()
{
    N = getint();
    S = N * N + 1;
    T = S + 1;
    for (int j = 1; j <= N; ++j)
        insert(j, T, getint());        
    for (int i = 1; i < N; ++i)
    for (int j = 1; j <= N; ++j)
        insert(i * N + j,
               (i - 1) * N + j,
               getint());
    for (int j = 1; j <= N; ++j)
        insert(S, (N - 1) * N + j, getint());
/*******************************************/
    for (int i = 0; i < N; ++i)
    {
        insert(S, i * N + 1, getint());
        for (int j = 1; j < N; ++j)
            insert(i * N + j,
                   i * N + j + 1,
                   getint());
        insert((i + 1) * N, T, getint());
    }
/*******************************************/
    for (int j = 1; j <= N; ++j) getint();
    for (int i = 1; i < N; ++i)
    for (int j = 1; j <= N; ++j)
        insert((i - 1) * N + j,
               i * N + j, getint());
    for (int j = 1; j <= N; ++j) getint();
/*******************************************/
    for (int i = 0; i < N; ++i)
    {
        getint();
        for (int j = 1; j < N; ++j)
            insert(i * N + j + 1,
                   i * N + j, getint());
        getint();
    }
    return;
}

inline void ad_down()
{
    for (int i = 2; i <= top; i <<= 1)
    {
        if (i < top && hp[i].d > hp[i + 1].d) ++i;
        if (hp[i >> 1].d > hp[i].d)
            swap(hp[i >> 1], hp[i]);
        else break;
    }
    return;
}

inline void ad_up(int i)
{
    for (; i >> 1; i >>= 1)
    if (hp[i >> 1].d > hp[i].d)
        swap(hp[i >> 1], hp[i]);
    else break;
    return;
}

inline int64 Dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[S] = 0;
    hp[++top] = Node(S, 0);
    ad_up(top);
    while (top)
    {
        int u = hp[1].ID;
        int64 d = hp[1].d;
        swap(hp[1], hp[top--]);
        ad_down();
        if (marked[u]) continue;
        marked[u] = 1;
        for (Edge *p = edge[u]; p; p = p -> next)
        {
            int v = p -> v;
            if (!marked[v] && d + p -> d < dist[v])
            {
                dist[v] = d + p -> d;
                hp[++top] = Node(v, dist[v]);
                ad_up(top);
            }
        }
    }
    return dist[T];
}

int main()
{
    init_file();
    readdata();
    printf("%I64d", Dijkstra());
    return 0;
}
priority_queue版:

#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>

using namespace std;
const char fi[] = "altitude.in";
const char fo[] = "altitude.out";
const int maxN = 250010; //500 * 500 = 250,000!!

typedef long long int64;
struct Edge
{
    int u, v;
    int64 d;
    Edge *next;
};

struct Node
{
    int ID;
    int64 d;
    Node() {}
    Node(int ID, int64 d): ID(ID), d(d) {}
    inline bool operator<(const Node &b) const
    {
        return d > b.d;
    }
};

Edge *edge[maxN];
priority_queue <Node> hp;
int64 dist[maxN];
bool marked[maxN];
int N, n, S, T, top;

void init_file()
{
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
    return;
}

inline int getint()
{
    int res = 0; char tmp;
    while (!isdigit(tmp = getchar()));
    do res = (res << 3) + (res << 1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return res;
}

inline void insert(int u, int v, int64 d)
{
    Edge *p = new Edge;
    p -> v = v;
    p -> d = d;
    p -> next = edge[u];
    edge[u] = p;
    return;
}

void readdata()
{
    N = getint();
    S = N * N + 1;
    T = S + 1;
    for (int j = 1; j <= N; ++j)
        insert(j, T, getint());        
    for (int i = 1; i < N; ++i)
    for (int j = 1; j <= N; ++j)
        insert(i * N + j, (i - 1) * N + j, getint());
    for (int j = 1; j <= N; ++j)
        insert(S, (N - 1) * N + j, getint());
/*******************************************/
    for (int i = 0; i < N; ++i)
    {
        insert(S, i * N + 1, getint());
        for (int j = 1; j < N; ++j)
            insert(i * N + j, i * N + j + 1, getint());
        insert((i + 1) * N, T, getint());
    }
/*******************************************/
    for (int j = 1; j <= N; ++j) getint();
    for (int i = 1; i < N; ++i)
    for (int j = 1; j <= N; ++j)
        insert((i - 1) * N + j, i * N + j, getint());
    for (int j = 1; j <= N; ++j) getint();
/*******************************************/
    for (int i = 0; i < N; ++i)
    {
        getint();
        for (int j = 1; j < N; ++j)
            insert(i * N + j + 1, i * N + j, getint());
        getint();
    }
    return;
}

inline int64 Dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[S] = 0;
    hp.push(Node(S, 0));
    while (!hp.empty())
    {
        int u = hp.top().ID;
        int64 d = hp.top().d;
        hp.pop();
        if (marked[u]) continue;
        marked[u] = 1;
        for (Edge *p = edge[u]; p; p = p -> next)
        {
            int v = p -> v;
            if (!marked[v] && d + p -> d < dist[v])
            {
                dist[v] = d + p -> d;
                hp.push(Node(v, dist[v]));
            }
        }
    }
    return dist[T];
}

int main()
{
    init_file();
    readdata();
    printf("%I64d", Dijkstra());
    return 0;
}

第二次做:

建图错了,就错了70分,仅仅是把i + 1和i打反了。
没有用堆优化,剩下两组超时(开始想的是万一人的流量为负怎么办……)。

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
#include <queue>
#define pos(i, j) ((i) * n + (j) + 1)

using namespace std;
const int maxN = 510 * 510, SIZE = 0x7ffff;
typedef pair <int, int> P; 
struct Edge
{
	int v, d; Edge *next; Edge() {}
	Edge(int v, int d, Edge *next):
		v(v), d(d), next(next) {}
} *edge[maxN]; bool marked[maxN];
int q[SIZE + 1], dist[maxN], n, S, T;
priority_queue <P, vector <P>, greater <P> > hp;

inline int getint()
{
	int res = 0; char tmp; bool sgn = 1;
	do tmp = getchar();
	while (!isdigit(tmp) && tmp - '-');
	if (tmp == '-') sgn = 0, tmp = getchar();
	do res = (res << 3) + (res << 1) + tmp - '0';
	while (isdigit(tmp = getchar()));
	return sgn ? res : -res;
}

inline void Ins(int u, int v, int d)
{edge[u] = new Edge(v, d, edge[u]); return;}

int main()
{
	freopen("altitude.in", "r", stdin);
	freopen("altitude.out", "w", stdout);
	n = getint(); S = n * n + 1; T = n * n + 2;
	for (int j = 0; j < n; ++j)
		Ins(pos(0, j), T, getint());
	for (int i = 0; i < n - 1; ++i)
	for (int j = 0; j < n; ++j)
		Ins(pos(i + 1, j), pos(i, j), getint());
	//建图不要反了!
	for (int j = 0; j < n; ++j)
		Ins(S, pos(n - 1, j), getint());
/**************************************************/
	for (int i = 0; i < n; ++i)
	{
		Ins(S, pos(i, 0), getint());
		for (int j = 0; j < n - 1; ++j)
			Ins(pos(i, j), pos(i, j + 1), getint());
		Ins(pos(i, n - 1), T, getint());
	}
/**************************************************/
	for (int j = 0; j < n; ++j) getint();
	for (int i = 0; i < n - 1; ++i)
	for (int j = 0; j < n; ++j)
		Ins(pos(i, j), pos(i + 1, j), getint());
	//建图不要反了!
	for (int j = 0; j < n; ++j) getint();
/**************************************************/
	for (int i = 0; i < n; ++i)
	{
		getint();
		for (int j = 0; j < n - 1; ++j)
			Ins(pos(i, j + 1), pos(i, j), getint());
		getint();
	}
	memset(dist, 0x3f, sizeof dist); dist[S] = 0;
	for (hp.push(make_pair(0, S)); !hp.empty();)
	{
		int u = hp.top().second, v; hp.pop();
		if (marked[u]) continue; marked[u] = 1;
		for (Edge *p = edge[u]; p; p = p -> next)
		if (!marked[v = p -> v] && dist[u] + p -> d < dist[v])
		//注意Dijkstra的写法。
			hp.push(make_pair(dist[v] = dist[u] + p -> d, v));
	}
	printf("%d\n", dist[T]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值