【BZOJ2595】游览计划(状压DP,斯坦纳树)

题意:见题面(我发现自己真是越来越懒了)


有N*M的矩阵,每个格子有一个值a[i,j]

现要求将其中的K个点(称为关键点)用格子连接起来,取(i,j)的费用就是a[i,j]

求K点全部连通的最小花费以及方案

n,m,k<=10

思路:斯坦纳树

虽然去年就疑似过了一道裸题,不过估计也是COPY的std,早就忘干净了

先%了一发论文,看到了几道有意思的SPFA的应用,准备去做一下

dp[i,j,sta]表示以(i,j)为根,关键点联通情况为sta的最小花费

显然初始化 \[ dp[i,j,1<<(k-1)]=0 (i,j为k号关键点)\]

\[ dp[i,j,sta]=dp[i,j,x]+dp[i,j,sta xor x]-a[i,j] \] 即合并子集

\[ dp[i,j,sta]=dp[x,y,sta]+a[i,j]\]  即合并道路

第一个转移可以枚举子集

第二个转移可能有环且形式是最短路,使用SPFA队列更新

类似一个分层图,SPFA的时候只用跑本层的内容

以下转自某大神blog:进行spfa的时候只需要对当前层的节点进行spfa就行了,不需要整个图完全松弛一遍,因为更高的层都可以通过枚举子集而变成若干个更低的层

时间复杂度:SPFA显然O(2^k)

枚举子集的dp时每个点有3个状态:不是子集,是子集但没取到,是子集且枚举到

所以O(3^k)

总时间复杂度O(3^k*n*m)

PS:其实暴力的想法:Sigma C(k,k-i)*2^i用二项式展开就是3^k啦(感谢邻桌数学国家队CWY同学)

 

  1 const oo=2000000000;
  2       dx:array[1..4]of longint=(-1,1,0,0);
  3       dy:array[1..4]of longint=(0,0,-1,1);
  4 var dp:array[1..12,1..12,0..1100]of longint;
  5     pre:array[1..12,1..12,0..1100,1..3]of longint;
  6     flag,a,inq:array[1..12,1..12]of longint;
  7     q:array[0..2000,1..3]of longint;
  8     n,m,i,j,x,y,k,v,k1,t,w,sta,tmp,ux,uy:longint;
  9 
 10 procedure dfs(i,j,sta:longint);
 11 var x,y,z:longint;
 12 begin
 13  if sta=0 then exit;
 14  flag[i,j]:=1;
 15  x:=pre[i,j,sta,1]; y:=pre[i,j,sta,2]; z:=pre[i,j,sta,3];
 16  dfs(x,y,z);
 17  if (x=i)and(y=j) then dfs(x,y,sta xor z);
 18 end;
 19 
 20 procedure print(x,y:longint);
 21 var i,j:longint;
 22 begin
 23  writeln(dp[x,y,(1<<k1)-1]);
 24  fillchar(flag,sizeof(flag),0);
 25  dfs(x,y,(1<<k1)-1);
 26  for i:=1 to n do
 27  begin
 28   for j:=1 to m do
 29    if a[i,j]>0 then
 30    begin
 31     if flag[i,j]=1 then write('o')
 32      else write('_');
 33    end
 34     else write('x');
 35   writeln;
 36  end;
 37 end;
 38 
 39 begin
 40  assign(input,'bzoj2595.in'); reset(input);
 41  assign(output,'bzoj2595.out'); rewrite(output);
 42  readln(n,m);
 43  for i:=1 to n do
 44   for j:=1 to m do read(a[i,j]);
 45  fillchar(dp,sizeof(dp),$7f);
 46  for i:=1 to n do
 47   for j:=1 to m do
 48    if a[i,j]=0 then
 49    begin
 50     inc(k1); dp[i,j,1<<(k1-1)]:=0;
 51    end;
 52  for v:=1 to (1<<k1)-1 do
 53  begin
 54   fillchar(inq,sizeof(inq),0);
 55   t:=0; w:=0;
 56   for i:=1 to n do
 57    for j:=1 to m do
 58    begin
 59     x:=v and (v-1);
 60     while x>0 do
 61     begin
 62      tmp:=dp[i,j,x]+dp[i,j,v xor x]-a[i,j];
 63      if tmp<dp[i,j,v] then
 64      begin
 65       dp[i,j,v]:=tmp;
 66       pre[i,j,v,1]:=i; pre[i,j,v,2]:=j; pre[i,j,v,3]:=x;
 67      end;
 68      x:=v and (x-1);
 69     end;
 70     if dp[i,j,v]<oo then begin inc(w); q[w,1]:=i; q[w,2]:=j; inq[i,j]:=1; end;
 71    end;
 72    while t<w do
 73    begin
 74     inc(t); ux:=q[t mod 2000,1]; uy:=q[t mod 2000,2]; inq[ux,uy]:=0;
 75     for k:=1 to 4 do
 76     begin
 77      x:=ux+dx[k]; y:=uy+dy[k];
 78      if (x>0)and(x<=n)and(y>0)and(y<=m)and(dp[ux,uy,v]+a[x,y]<dp[x,y,v]) then
 79      begin
 80       dp[x,y,v]:=dp[ux,uy,v]+a[x,y];
 81       pre[x,y,v,1]:=ux; pre[x,y,v,2]:=uy; pre[x,y,v,3]:=v;
 82       if inq[x,y]=0 then
 83       begin
 84        inc(w); q[w mod 2000,1]:=x; q[w mod 2000,2]:=y; inq[x,y]:=1;
 85       end;
 86      end;
 87     end;
 88    end;
 89  end;
 90 
 91 
 92 
 93  for i:=1 to n do
 94  begin
 95   for j:=1 to m do
 96    if a[i,j]=0 then begin print(i,j); break; end;
 97   if a[i,j]=0 then break;
 98  end;
 99  close(input);
100  close(output);
101 end.

 

 

 

 

 

posted on 2016-12-22 20:31  myx12345  阅读(192)  评论(0编辑  收藏  举报

导航