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

时间:2021-06-20 18:48:32

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

有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同学)

 const oo=;
dx:array[..]of longint=(-,,,);
dy:array[..]of longint=(,,-,);
var dp:array[..,..,..]of longint;
pre:array[..,..,..,..]of longint;
flag,a,inq:array[..,..]of longint;
q:array[..,..]of longint;
n,m,i,j,x,y,k,v,k1,t,w,sta,tmp,ux,uy:longint; procedure dfs(i,j,sta:longint);
var x,y,z:longint;
begin
if sta= then exit;
flag[i,j]:=;
x:=pre[i,j,sta,]; y:=pre[i,j,sta,]; z:=pre[i,j,sta,];
dfs(x,y,z);
if (x=i)and(y=j) then dfs(x,y,sta xor z);
end; procedure print(x,y:longint);
var i,j:longint;
begin
writeln(dp[x,y,(<<k1)-]);
fillchar(flag,sizeof(flag),);
dfs(x,y,(<<k1)-);
for i:= to n do
begin
for j:= to m do
if a[i,j]> then
begin
if flag[i,j]= then write('o')
else write('_');
end
else write('x');
writeln;
end;
end; begin
assign(input,'bzoj2595.in'); reset(input);
assign(output,'bzoj2595.out'); rewrite(output);
readln(n,m);
for i:= to n do
for j:= to m do read(a[i,j]);
fillchar(dp,sizeof(dp),$7f);
for i:= to n do
for j:= to m do
if a[i,j]= then
begin
inc(k1); dp[i,j,<<(k1-)]:=;
end;
for v:= to (<<k1)- do
begin
fillchar(inq,sizeof(inq),);
t:=; w:=;
for i:= to n do
for j:= to m do
begin
x:=v and (v-);
while x> do
begin
tmp:=dp[i,j,x]+dp[i,j,v xor x]-a[i,j];
if tmp<dp[i,j,v] then
begin
dp[i,j,v]:=tmp;
pre[i,j,v,]:=i; pre[i,j,v,]:=j; pre[i,j,v,]:=x;
end;
x:=v and (x-);
end;
if dp[i,j,v]<oo then begin inc(w); q[w,]:=i; q[w,]:=j; inq[i,j]:=; end;
end;
while t<w do
begin
inc(t); ux:=q[t mod ,]; uy:=q[t mod ,]; inq[ux,uy]:=;
for k:= to do
begin
x:=ux+dx[k]; y:=uy+dy[k];
if (x>)and(x<=n)and(y>)and(y<=m)and(dp[ux,uy,v]+a[x,y]<dp[x,y,v]) then
begin
dp[x,y,v]:=dp[ux,uy,v]+a[x,y];
pre[x,y,v,]:=ux; pre[x,y,v,]:=uy; pre[x,y,v,]:=v;
if inq[x,y]= then
begin
inc(w); q[w mod ,]:=x; q[w mod ,]:=y; inq[x,y]:=;
end;
end;
end;
end;
end; for i:= to n do
begin
for j:= to m do
if a[i,j]= then begin print(i,j); break; end;
if a[i,j]= then break;
end;
close(input);
close(output);
end.