1. 程式人生 > >訪問(最短路+搜尋剪枝)

訪問(最短路+搜尋剪枝)

2247: 訪問(deliver)

題目描述

給你一個n個頂點的鄰接矩陣(圖),以及每個頂點的訪問時限,要求從頂點1開始,尋找一個訪問序列,要求在每個頂點的訪問時限之前訪問,且每個頂點的訪問時間之和最小

輸入

第一行一個數n,2<=n<=30。 下面n行為一個n*n的鄰接矩陣,其中的第i行第j列元素值為頂點i到頂點j之間的消耗時間(integer以內)。  最後一行為n-1個數(longint以內),分別表示第2到第n個頂點的訪問時限。

輸出

一行一個數,表示按要求訪問完所有頂點的最少時間。如果不能完成則輸出一個-1。

樣例輸入

4 0 3 8 6 4 0 7 4 7 5 0 2 6 9 3 0 30 8 30

樣例輸出

36

剛看到這道題時,我認真地看了n多遍的樣例。怎麼算都是37,於是我愉快地認為樣例錯了。然後就GG了。

先解釋一波樣例,n=4,然後一個4*4的矩陣,用來描述兩點之間的距離,最後一行描述2~4個點的訪問時限。

根據2,3,4的時限分別為30,8,30可知,必須先從1到3。然後可以由3到2或4,顯然,先到4所用時間較少。最後從4到2(關鍵來了),這時候,我就認為從4直接到2,於是8+(8+2)+(8+2+9)=37(注意,是每個點的訪問時間之和,所以時間要累加),然後就GG了。然而,正解是這樣的,從4先到3在從3到2。那麼答案就變成了8+(8+2)+(8+2+3+5)=36(很重要的一點,點是可以重複走來使時間消耗盡量少的)。所以,很明顯可以用最短路的思想,又由於本題n範圍較小,所以直接弗洛伊德。求出最短路後,再進行回溯搜尋,不斷模擬路徑。當然直接搜尋是行不通的,絕對是會超時的。所以需要加上剪枝。兩個比較好想的剪枝。

1、根據答案最小剪枝,噹噹前的總時間+訪問到當前這個點的時間*剩下的點數(假設到剩下的點的時間都為0)>最小總時間時exit

2、根據每個點的時限剪枝,for迴圈一遍搜尋,一但當前時間+當前所在的點到i點的時間超過i點的時限時exit

貼個程式碼(pascal)

var n,i,j,min,k:longint;
a,f:array[1..2000,1..2000] of longint;
p,t:array[1..100000] of longint;
function min1(x,y:longint):longint;
 begin
  if x>y then exit(y)
  else exit(x);
 end;
procedure dfs(t1,t2,ans:longint);{t1為當前到達的點,t2為當前已經做到第t2個點,ans為當前的總時間}
var i:longint;
 begin
  if t2=n then begin if ans<min then min:=ans; exit;end;
  for i:=2 to n do
   if (p[i]=0)and(p[t1]+f[t1,i]>t[i]) then exit;{根據時限剪枝}
    if ans+p[t1]*(n-t2)>=min then exit;{根據答案剪枝}
  for i:=2 to n do
   begin
    if p[i]=0 then
     begin
      p[i]:=p[t1]+f[t1,i];
      dfs(i,t2+1,ans+p[i]);
      p[i]:=0;
     end;
   end;
 end;
begin
   min:=maxlongint;
   readln(n);
   fillchar(a,sizeof(a),$7f);
   fillchar(p,sizeof(p),0);
   for i:=1 to n do
    for j:=1 to n do
     begin
      read(a[i,j]);
     end;
   f:=a;
   for i:=2 to n do
    read(t[i]);
   for k:=1 to n do
    for i:=1 to n do
     begin
     if i=k then continue;
     for j:=1 to n do
      begin
       if i=j then continue;
       if j=k then continue;
       f[i,j]:=min1(f[i,j],f[i,k]+f[k,j]);
      end;
     end;
   dfs(1,1,0);
   if min=maxlongint then writeln(-1) else writeln(min);
end.