noi 2010 海拔

【問題描述】 

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)條邊都是按照從左到右再從上到下給的

搞定了讀入,再建圖就沒有什麼困難了。

然後就沒有了。

 

AC CODE

 

program noi_2010_day1_altitude;

var line,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;

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

procedure ins(x,y,z:longint);

begin

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

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

end;

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

procedure init;

var i,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;

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

procedure swap(x,y:longint);

var t:longint;

begin

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

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

end;

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

procedure update(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;

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

procedure insert(x:longint);

var i:longint;

begin

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

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

  update(size);

end;

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

procedure del;

var i,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;

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

procedure dij_heap;

var i,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.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章