noi 2010 海拔

来源:互联网 发布:联合国一票否决权 知乎 编辑:程序博客网 时间:2024/04/28 23:27

【问题描述】 

YT市是一个规划良好的城市,城市被东西向和南北向的主干道划分为 n×n 个区域。简

单起见,可以将 YT市看作 一个正方形,每一个区域也可看作一个正方形。从而,YT 城市

中包括(n+1)×(n+1)个交叉路口和2n×(n+1)条双向道路(简称道路),每条双向道路连接主干

道上两个相邻的交叉路口。下图为一张 YT 市的地图(n =2),城市被划分为2×2 个区域,包

3×3 个交叉路口和 12条双向道路。

noi <wbr>2010 <wbr>海拔

Z 作为该市的市长,他根据统计信息得到了每天上班高峰期间 YT 市每条道路两个方

向的人流量,即在高峰期间沿 着该方向通过这条道路的人数。每一个交叉路口都有不同的

海拔高度值,YT市市民认为爬坡是一件非常累的事情,每向上爬 h 的高度,就需要消耗 h

的体力。如果 是下坡的话,则不需要耗费体力。因此如果一段道路的终点海拔减去起点海

拔的值为 h(注意h 可能是负数),那么一个人经过这段路所消耗的体力是max{0, h}(这里

max{a,b}表示取a, b 两个值中的较大值)。

Z 还测量得到这个城市西北角的交叉路口海拔为 0,东南角的交叉路口海拔为1(如上

图所示),但其它交叉路口的海拔高度都无法得知。小Z 想知道在最理想的情况下(即你可

以任意假设其他路口的海拔高度),每天上班高峰期间所有人爬坡消耗的总体力和的最小值。

【输入格式】 

输入文件 altitude.in第一行包含一个整数 n,含义如上文所示。

接下来 4n(n +1)行,每行包含一个非负整数分别表示每一条道路每一个方向的人流量信

息。输入顺序:n(n +1)个数表示所有从西到东方向的人流量,然后n(n + 1)个数表示所有从

北到南方向的人流量,n(n +1)个数表示所有从东到西方向的人流量,最后是n(n + 1)个数表

示所有从南到北方向的人流量。对于每一个方向,输入顺序按照起点由北向南,若南北方向

相同时由西到东的顺序给出(参见样例输入)

【输出格式】 

输出文件 altitude.out仅包含一个数,表示在最理想情况下每天上班高峰期间所有人爬

坡所消耗的总体力和(即总体力和的最小值),结果四舍五入到整数。

全国青少年奥林匹克竞赛(NOI2010 试题

Copyright© 2010 中国计算机学会,版权所有.

【样例输入】 

1

1

2

3

4

5

6

7

8

【样例输出】 

3

【样例说明】 

样例数据见下图。

 

 noi <wbr>2010 <wbr>海拔

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

【数据规模】 

对于 20%的数据:n 3

对于 50%的数据:n 15

对于 80%的数据:n 40

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

【提示】 

海拔高度不一定是整数。

【运行时限】 

2秒。

【运行空限】 

512M

 

第一眼看题目有木有很蛋疼。。海拔可以随便取,还不一定是整数。。。

但是想:找出图中海拔最高的点,如果它不是起止点,那么总是可以把它调小使得结果更优。那么最高点最后就必然为终点。同理可知最低点必然为起点。那么图中有没有可能存在小数使得结果更优呢?同样可以把小数向上或向下调整到10使得代价不增。

再有,所有海拔为0的点必然互相连通。为什么呢?显然,四面连接10如果使它取1必然使结果更优。同理可推海拔为1的点。

那么得到了大致的图后,很明显,就是一个最小割的问题了。

把每边消耗的体力设为这条边的流量,求出割,分成海拔分别为01的两块。

但是最后两点必超无疑。。。

有没有时效更高的做法呢?用最短路来求割

在题目所给图中的左下角和右上角加入源和汇,如下建立原图的对偶图(红色部分)

noi <wbr>2010 <wbr>海拔

 

摘自:http://blog.sina.com.cn/s/blog_86942b1401014ajk.html

可以看出,每一条最短路必然把地图“切成”两块,那么每条连接源和汇的路径就是一组合法的割了,求出最短路,就相当于求出最小割、

只要最短路,强烈建议编dij+heap,第一次想当然编了spfa倒二点4+s。。。不过敲了下dij+heap的模版还是很有感觉的。

最后的最后,不得不说下读入。。。什么南北东西的南方人表示理解不能。。。连个优先级都没搞清楚。结果建图的地方借用了上面blog中的做法。。。鸣谢、

对于每组n*(n+1)条边都是按照从左到右再从上到下给的

搞定了读入,再建图就没有什么困难了。

然后就没有了。

 

ACCODE

 

programnoi_2010_day1_altitude;

varline,next,g:array[1..2000000] of longint;

   heap,pi,dis:array[1..250002] of longint;   //pi数组记录节点到堆中的映射。

   p:array[1..250002] of boolean;

   en:array[1..250002] of longint;

   n,m,i,j,s,t,tot,size:longint;

//============================================================================

procedureins(x,y,z:longint);

begin

 inc(tot); line[tot]:=y; g[tot]:=z;

 next[tot]:=en[x]; en[x]:=tot;

end;

//============================================================================

procedureinit;

vari,j,x:longint;

begin

 readln(n); m:=n*(n+1);

 s:=n*n+1; t:=n*n+2;

 for i:=1 to n do

 begin

   read(x); ins(i,t,x);

 end;

 for i:=1 to n-1 do

   for j:=1 to n do

   begin

     read(x); ins(i*n+j,(i-1)*n+j,x);

   end;

 for i:=1 to n do

 begin

   read(x); ins(s,(n-1)*n+i,x);

 end;

 for i:=0 to n-1 do

 begin

   read(x); ins(s,i*n+1,x);

   for j:=1 to n-1 do

   begin

     read(x); ins(i*n+j,i*n+j+1,x);

   end;

   read(x); ins(i*n+n,t,x);

 end;

 for i:=1 to n do read(x);

 for i:=1 to n-1 do

   for j:=1 to n do

   begin

     readln(x); ins((i-1)*n+j,i*n+j,x);

   end;

 for j:=1 to n do read(x);

 for i:=0 to n-1 do

 begin

   read(x);

   for j:=1 to n-1 do

   begin

     read(x); ins(i*n+j+1,i*n+j,x);

   end; read(x);

 end;

end;

//============================================================================

procedureswap(x,y:longint);

vart:longint;

begin

 t:=heap[x]; heap[x]:=heap[y]; heap[y]:=t;

 pi[heap[x]]:=x; pi[heap[y]]:=y;

end;

//============================================================================

procedureupdate(x:longint);

begin

 while x>1 do

   if dis[heap[x]]<dis[heap[x shr 1]] then

   begin

     swap(x,x shr 1);

     x:=x shr 1;

   end else break;

end;

//============================================================================

procedureinsert(x:longint);

vari:longint;

begin

 inc(size); heap[size]:=x;

 pi[x]:=size; p[x]:=true;   //入堆的标记要打。

 update(size);

end;

//============================================================================

proceduredel;

vari,g,h,l,r,m:longint;

begin

 swap(1,size); p[heap[size]]:=false;   //弹出的时候要取消标记。

 pi[heap[size]]:=0; dec(size); i:=1;

 while i<=size do

 begin m:=heap[i];

   g:=i shl 1; h:=i shl 1+1;

   l:=heap[g]; r:=heap[h];

   if (g<=size) and (dis[m]>dis[l])then   //向下维护的时候老是忘了判断左右儿子是否在堆中。。。浪费时间调。。。

   begin

     if (h<=size) and (dis[r]<dis[l])then

     begin

       swap(i,h);

       i:=h;

     end else

     begin

       swap(i,g);

       i:=g;

     end;

   end else

   begin

     if (h<=size) and (dis[m]<dis[r])then

     begin

       swap(i,h);

       i:=h;

     end else break;

   end;

 end;

end;

//============================================================================

proceduredij_heap;

vari,u,v:longint;

begin

 fillchar(p,sizeof(p),0);  //判断是否在堆中,添加和取消标记都是在子程序中操作不容易乱。

 for i:=1 to n*n+2 do dis[i]:=maxlongint div 3;  //一开始初值没赋。。不要赋值成maxlongint,会爆。

 insert(s); dis[s]:=0;

 while size>0 do

 begin

   u:=heap[1]; del; i:=en[u];

   while i<>0 do

   begin v:=line[i];

     if dis[u]+g[i]<dis[v] then

     begin

       dis[v]:=dis[u]+g[i];

       if not(p[v]) then insert(v) else

         update(pi[v]);   //有修改就必然要有入堆或更新操作。

     end; i:=next[i];

   end;

 end; writeln(dis[t]);

end;

//============================================================================

begin

 assign(input,'altitude.in');

 assign(output,'altitude.out');

  reset(input);rewrite(output);

 init;

 dij_heap;

 close(input); close(output);

end.

0 0
原创粉丝点击