一开始看是插头dp,后来发现还有一个叫斯坦纳树的东西

什么叫斯坦纳树,就是使给定点连通开销和最小的树(可以包含多余的点)

到这张平面图上,我们不难想到用dp来解决,设f[x,y,S]表示连通集合为S,树根为点(x,y)的最小开销

不难得到两个方程式

f[x,y,S]=min(f[x,y,s']+f[x,y,S-s']-a[x,y]) S'是S的一个子集,相当于合并两个数

f[x,y,S]=min(f[x',y',S]+a[x,y]) (x',y')与(x,y)相邻

由于景点很少,我们显然可以用状压dp,初始f[景点坐标,景点状态]=0

第一个方程转移大家都会,第二个方程转移是没有明确转移顺序,只要转移起点,因此我们用spfa转移

所以总的处理转移,我们穷举连通状况,然后先用第一个方程转移,然后再用第二个方程转移

  1 const dx:array[1..4] of longint=(0,0,1,-1);
  2       dy:array[1..4] of longint=(1,-1,0,0);
  3       inf=1000000007;
  4 type node=record
  5        x,y,k:longint;
  6      end;
  7 
  8 var q:array[0..1000010] of node;
  9     pre:array[0..11,0..11,0..1025] of node;
 10     f:array[0..11,0..11,0..1025] of longint;
 11     v:array[0..11,0..11] of boolean;
 12     a:array[0..11,0..11] of longint;
 13     t,h,r,k,i,j,s,n,m,x,y:longint;
 14 
 15 function make(i,j,k:longint):node;
 16   begin
 17     make.x:=i;
 18     make.y:=j;
 19     make.k:=k;
 20   end;
 21 
 22 procedure spfa(k:longint);
 23   var i,x,y,x0,y0:longint;
 24   begin
 25     while h<=r do
 26     begin
 27       x0:=q[h].x; y0:=q[h].y;
 28       v[x0,y0]:=false;
 29       for i:=1 to 4 do
 30       begin
 31         x:=x0+dx[i];
 32         y:=y0+dy[i];
 33          if (x<0) or (x>n) or (y<0) or (y>m) then continue;
 34         if f[x,y,k]>f[x0,y0,k]+a[x,y] then
 35         begin
 36           f[x,y,k]:=f[x0,y0,k]+a[x,y];
 37           pre[x,y,k]:=make(x0,y0,k);
 38           if not v[x,y] then
 39           begin
 40             v[x,y]:=true;
 41             inc(r);
 42             q[r].x:=x; q[r].y:=y;
 43           end;
 44         end;
 45       end;
 46       inc(h);
 47     end;
 48   end;
 49 
 50 procedure dfs(x,y,k:longint);
 51   var m:node;
 52   begin
 53     v[x,y]:=true;
 54     m:=pre[x,y,k];
 55     if m.x=0 then exit;
 56     dfs(m.x,m.y,m.k);
 57     if (m.x=x) and (m.y=y) then dfs(m.x,m.y,k-m.k);
 58   end;
 59 
 60 begin
 61   readln(n,m);
 62   for i:=1 to n do
 63     for j:=1 to m do
 64       for k:=0 to 1024 do
 65         f[i,j,k]:=inf;
 66   for i:=1 to n do
 67     for j:=1 to m do
 68     begin
 69       read(a[i,j]);
 70       if a[i,j]=0 then
 71       begin
 72         f[i,j,1 shl t]:=0;
 73         inc(t);
 74       end;
 75     end;
 76   h:=1;
 77   r:=0;
 78   for k:=1 to 1 shl t-1 do
 79   begin
 80     for i:=1 to n do
 81       for j:=1 to m do
 82       begin
 83         s:=k and (k-1);
 84         while s<>0 do  //穷举子集
 85         begin
 86           if f[i,j,k]>f[i,j,s]+f[i,j,k-s]-a[i,j] then
 87           begin
 88             f[i,j,k]:=f[i,j,s]+f[i,j,k-s]-a[i,j];
 89             pre[i,j,k]:=make(i,j,s); //记录从哪转移来的
 90           end;
 91           s:=k and (s-1);
 92         end;
 93         if f[i,j,k]<inf then
 94         begin
 95           v[i,j]:=true;
 96           inc(r);
 97           q[r].x:=i; q[r].y:=j;
 98         end;
 99       end;
100     spfa(k);
101     h:=1;
102     r:=0;
103   end;
104   for i:=1 to n do
105     for j:=1 to m do
106       if a[i,j]=0 then
107       begin
108         x:=i;
109         y:=j;
110         break;
111       end;
112 
113   writeln(f[x,y,1 shl t-1]);
114   dfs(x,y,1 shl t-1);
115   for i:=1 to n do
116   begin
117     for j:=1 to m do
118       if a[i,j]=0 then write('x')
119       else if v[i,j] then write('o')
120       else write('_');
121     writeln;
122   end;
123 end.
View Code

 

posted on 2015-05-09 16:35  acphile  阅读(290)  评论(0编辑  收藏  举报