2247: 访问(deliver)
题目描述
给你一个n个顶点的邻接矩阵(图),以及每个顶点的访问时限,要求从顶点1开始,寻找一个访问序列,要求在每个顶点的访问时限之前访问,且每个顶点的访问时间之和最小
输入
输出
一行一个数,表示按要求访问完所有顶点的最少时间。如果不能完成则输出一个-1。
样例输入
样例输出
刚看到这道题时,我认真地看了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)
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.

737

被折叠的 条评论
为什么被折叠?



