[bzoj2007][网络流-最小割][平面图转对偶图]海拔

35 篇文章 0 订阅
1 篇文章 0 订阅

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

HINT

【样例说明】

样例数据见下图。

最理想情况下所有点的海拔如上图所示。

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

题解

纪念一下第一篇对偶图
首先大胆猜测一个结论
海拔只有0和1
不然这么花里胡哨怎么做嘛
于是可以跑一个最小割
只需要知道0和1的分界线是什么
那么流量设为边权就可以裸最小割了
虽然dinic跑不过
然后这是一个平面图,可以转对偶图求最短路
转对偶图大概就是把一个四条边围成的区域看成一个点
然后点与点之间互相连边,边权yy一下
大概长这个样子
在这里插入图片描述
容易发现,从S到T的每一条路径都是一个割
固定分界线右边/上面是1的部分,那么边权很好yy
跑一个dij就可以了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#include<iostream>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;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;
}
int stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=505;

struct edge{int x,y,c,next;}a[MAXN*MAXN*4];int len,last[MAXN*MAXN];
void ins(int x,int y,int c){len++;a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;}

bool vis[MAXN*MAXN];
LL d[MAXN*MAXN];
priority_queue<pll,vector<pll>,greater<pll> > hp;
void dij(int st)
{
	memset(d,63,sizeof(d));d[st]=0;
	hp.push(mp(0,st));
	while(!hp.empty())
	{
		int x=hp.top().second;hp.pop();
		if(vis[x])continue;vis[x]=1;
		for(int k=last[x];k;k=a[k].next)
		{
			int y=a[k].y;
			if(d[y]>d[x]+a[k].c)
			{
				d[y]=d[x]+a[k].c;
				hp.push(mp(d[y],y));
			}
		}
	}
}
int n;
int pt(int x,int y){return (x-1)*n+y;}
int main()
{
	n=read();
	int st=n*n+1,ed=n*n+2;
	for(int i=1;i<=n+1;i++)
		for(int j=1;j<=n;j++)
		{
			int c=read();
			if(i-1==0)ins(pt(i,j),ed,c);
			else if(i==n+1)ins(st,pt(i-1,j),c);
			else ins(pt(i,j),pt(i-1,j),c);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n+1;j++)
		{
			int c=read();
			if(j-1==0)ins(st,pt(i,j),c);
			else if(j==n+1)ins(pt(i,j-1),ed,c);
			else ins(pt(i,j-1),pt(i,j),c);
		}
	for(int i=1;i<=n+1;i++)
		for(int j=1;j<=n;j++)
		{
			int c=read();
			if(i-1==0)continue;
			else if(i==n+1)continue;
			else ins(pt(i-1,j),pt(i,j),c);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n+1;j++)
		{
			int c=read();
			if(j-1==0)continue;
			else if(j==n+1)continue;
			else ins(pt(i,j),pt(i,j-1),c);
		}
	dij(st);
	pr2(d[ed]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值