[NOI2010]海拔

2 篇文章 0 订阅
1 篇文章 0 订阅

题目描述:

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)个数表示所有从南到北方向的人流量。对于每一个方向,输入顺序按照起点由北向南,若南北方向相同时由西到东的顺序给出(参见样例输入)。

输出格式:

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

解法:

20%解法:

20%的数据中,n最多才3,也就是最多才16个点。首先,这道题题目的意思是其他点的海拔可以为任意值(小数也可以),但经过分析很快可以发现一个点的海拔如果与它上下左右四个方向的点的海拔都不同的话,可以将将它变成和其中的某个点海拔一致以让方案更优,所以,想到这里,我们就可以得出所有的点都只有0和1两种取值的结论,便可以搜索来判断。

时间复杂度:

50%解法:

然而我并不知道这个分数是给什么做法的~_~

80%解法:

这时候,我们再进一步想,如果一个0的联通块的四周都是1,而且这个联通块不包含左上角的0,那么显然这些0都变成1会更优,一个不包含右下角的1的联通块若四周都是0,则变成0会更优,通过这个简单的分析,我们又得出了一个重要的结论,必然所有的0构成一个联通块,所有的1构成一个联通块,这样,我们就发现这是一个典型的最小割模型,跑网络流!源点是左上角,汇点是右下角,而输入的流量就是网络流边权。时间复杂度较为玄学,但dinic跑掉80%的数据是完全不虚的。

100%解法:

只要在80%的解法上再优化一点就好了,注意到这是个网格图:平面图最小割=最大流=对偶图最短路。具体将原图转成对偶图的方法是:将图中原图的源点和汇点连一条虚边,将外界隔成2个面,将源点和汇点顺时针旋转90度,到达哪个面就将那个面看成是源点或汇点,将一个联通的面看成一个点。将原图的边也顺时针旋转90度,就让起点和结束点分到了两个不同的面上,就在这两个面所代表的点上连边,最后跑一边spfa,记得加优化,即当前队头的dis值大于队尾的dis值就将队头队尾交换,不然可能t掉。

                                       

                                                                  推荐番:《CLANNAD》

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=6000005;
int ope[maxn],h,t,zhi[3000005],pan[3000005],pp[3000005],v[3000005],to[3000005],next[3000005],n,val,p;
void spfa(int k)
{
	zhi[k]=0;
	ope[1]=k;h=1;t=1;pan[k]=1;
	while (h<=t)
	{
		int pu=pp[ope[h%maxn]];
		while (pu>0)
		{
			if (zhi[ope[h%maxn]]+v[pu]<zhi[to[pu]])
			{
				zhi[to[pu]]=zhi[ope[h%maxn]]+v[pu];
				if (pan[to[pu]]==0){t++;ope[t%maxn]=to[pu];pan[to[pu]]=1;}
			}
			pu=next[pu];
		}
		pan[ope[h%maxn]]=0;h++;if (zhi[ope[h%maxn]]>zhi[ope[t%maxn]])swap(ope[h%maxn],ope[t%maxn]);
	}
}
int main()
{
	cin>>n;
	for (int i=1;i<=n*(n+1);i++)
	{
		scanf("%d",&val);
		if (i<=n) {p++;to[p]=i;next[p]=pp[300000];pp[300000]=p;}
		else if (n*(n+1)-i<n){p++;to[p]=300001;next[p]=pp[i-n];pp[i-n]=p;}
		else {p++;to[p]=i;next[p]=pp[i-n];pp[i-n]=p;}
		v[p]=val;
	}
	for (int i=1;i<=300001;i++)zhi[i]=1000000007;
	for (int i=1;i<=n*(n+1);i++)
	{
		scanf("%d",&val);
		if (i%(n+1)==0){p++;to[p]=i/(n+1)*n;next[p]=pp[300000];pp[300000]=p;}
		else if (i%(n+1)==1){p++;to[p]=300001;next[p]=pp[(i/(n+1))*n+1];pp[(i/(n+1))*n+1]=p;}
		else {p++;to[p]=i/(n+1)*n+i%(n+1)-1;next[p]=pp[i/(n+1)*n+i%(n+1)];pp[i/(n+1)*n+i%(n+1)]=p;}
		v[p]=val;
	}
	for (int i=1;i<=n*(n+1);i++)
	{
		scanf("%d",&val);
		if (i>n && n*(n+1)-i>=n)
		{p++;to[p]=i-n;next[p]=pp[i];pp[i]=p;v[p]=val;}
	}
	for (int i=1;i<=n*(n+1);i++)
	{
		scanf("%d",&val);
		if (i%(n+1)!=1 && i%(n+1)!=0)
		{p++;to[p]=i/(n+1)*n+i%(n+1);next[p]=pp[i/(n+1)*n+i%(n+1)-1];pp[i/(n+1)*n+i%(n+1)-1]=p;v[p]=val;}
	}	
	spfa(300000);
	cout<<zhi[300001];
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值