【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.
null