【图论】Nosknah 的宝藏

问题

给你一个无向图,有n个节点m条双向边,其中有p个节点必须经过,求最短的从1经过这p个节点再到n的最短距离

数据范围

n<=200 p<=12;

分析

先用floyd处理处任意两点之间的最短距离。

之后的得到的距离只于遍历这p个点的顺序有关。

可以搜索,但是12的数据显然超时,考虑使用状态压缩的动态规划。

可以用二进制串m表示状态,1表示这个物品已选,0表示未选。

f[i,j]表示当前在选i,之前选取的全部状态是j

采用记忆化搜索的形式,方程f[i,j]:=min{f[p,j-p(不选p的状态)]+map[bw[p],i]}

可以枚举最后一个选的是k,然后算出此时相应的状态,的f值,再加上map[bw[k],n]即可。

这样复杂度是12*2^12

显然很优秀。相当于在不同的状态间进行转移,无后效性。

code

program liukeke;
const maxb:int64=100000000000000000;
var
  map:array[0..201,0..201] of int64;
  f:array[0..12,0..5000] of int64;
  bw:array[0..12] of longint;
  ans:int64;
  n,m,p:longint;

procedure init;
var
  i,j,x,y,z:longint;
begin
  readln(n,m);
  for i:=1 to n do
    for j:=1 to n do
      map[i,j]:=maxb;   
  for i:=1 to m do
  begin
    readln(x,y,z);
    if z<map[x,y] then
    begin
      map[x,y]:=z;
      map[y,x]:=z;
    end;
  end;
  for i:=1 to n do
    map[i,i]:=0;
  readln(p);
  for i:=1 to p do
    readln(bw[i]);
end;

procedure floyd;
var
  i,j,k:longint;
begin
  for k:=1 to n do
    for i:=1 to n do
      if i<>k then
        for j:=1 to n do
          if (i<>j)and(j<>k) then
            if map[i,j]>map[i,k]+map[k,j] then
              map[i,j]:=map[i,k]+map[k,j];
end;

function dfs(now,t:longint):int64;
var
  i,temp:longint;
begin
  if f[now,t]<maxb then exit(f[now,t]);
  if t=0 then
  begin
    f[now,t]:=map[1,bw[now]];
    exit(f[now,t]);
  end;
  for i:=1 to p do
  begin
    if (t and (1<<(i-1)) )=0 then continue;
    temp:=t-(1<<(i-1));
    if dfs(i,temp)+map[bw[i],bw[now]]<f[now,t] then
      f[now,t]:=dfs(i,temp)+map[bw[i],bw[now]]
  end;
  exit(f[now,t]);
end;

procedure dp;
var
  i,j,k:longint;
begin
  for i:=1 to p do
    if (map[1,bw[i]]=maxb)or(map[bw[i],n]=maxb) then
    begin
      writeln(-1);
      exit;
    end;
  if map[1,n]=maxb then
  begin
    writeln(-1);
    exit;
  end;
 
  ans:=maxb;
  for i:=1 to p do
    for j:=0 to 1<<p do
      f[i,j]:=maxb;

  for i:=1 to p do
  begin
    k:=1<<p-1-(1<<(i-1));
    if dfs(i,k)=maxb then
      begin
        writeln(-1);
        exit;
      end
      else
      if dfs(i,k)+map[bw[i],n]<ans then ans:=dfs(i,k)+map[bw[i],n];
  end;
  writeln(ans);
end;

begin
  assign(input,'gold.in');reset(input);
  assign(output,'gold.out');rewrite(output);
  init;
  floyd;
  dp;
  close(input);close(output);
end.

反思

在搜索超时时,考虑动态规划的算法,特别是范围比较小,可用二进制串表示状态时,用状态压缩的动态规划

posted @ 2011-10-04 19:39  liukee  阅读(328)  评论(0编辑  收藏  举报