【NOIP2017】宝藏(状压DP)

题意:

思路:n<=12,考虑状压DP

生成树中深度相同的点可以一次性转移完毕

设dp[sta,i]为已转移完sta状态的点,当前深度为i的最小花费

dp[sta or v,i+1]=min(dp[sta,i]+f[sta,v]*(i+1)),其中v是sta关于全集(1<<n)-1的补集v1的一个子集,这一步需要枚举子集

考场上写的O(3^n*n^2),没有预处理f[sta,v]而是每次都算了一遍,有进一步优化的空间

 1 const max=500001;
 2 var dp,dis:array[0..5000,0..20]of int64;
 3     f:array[1..20,1..20]of int64;
 4     n,m,i,j,x,y,z,k,maxs,v,v1:longint;
 5     s,ans:int64;
 6 
 7 function min(x,y:int64):int64;
 8 begin
 9  if x<y then exit(x);
10  exit(y);
11 end;
12 
13 begin
14  assign(input,'treasure.in'); reset(input);
15  assign(output,'treasure.out'); rewrite(output);
16  readln(n,m);
17  for i:=1 to n do
18   for j:=1 to n do f[i,j]:=1<<60;
19  for i:=1 to n do f[i,i]:=0;
20  for i:=1 to m do
21  begin
22   readln(x,y,z);
23   f[x,y]:=min(f[x,y],z);
24   f[y,x]:=min(f[y,x],z);
25  end;
26  maxs:=(1<<n)-1;
27  for i:=1 to maxs do
28   for j:=1 to n do
29    if i and (1<<(j-1))=0 then
30    begin
31     dis[i,j]:=1<<60;
32     for k:=1 to n do
33      if (j<>k)and(i and (1<<(k-1))>0) then dis[i,j]:=min(dis[i,j],f[j,k]);
34    end;
35 
36  m:=maxs;
37  for i:=1 to maxs do
38   for j:=0 to n+1 do dp[i,j]:=1<<60;
39  for i:=1 to n do dp[1<<(i-1),0]:=0;
40  for i:=0 to n do
41   for j:=1 to maxs do
42   begin
43    v:=j xor m; v1:=v;
44    while v>0 do
45    begin
46     s:=0;
47     for k:=1 to n do
48      if v and (1<<(k-1))>0 then
49      begin
50       s:=s+dis[j,k];
51       if s>=(1<<60) then break;
52      end;
53     if s<(1<<60) then
54      dp[j or v,i+1]:=min(dp[j or v,i+1],dp[j,i]+s*(i+1));
55     v:=v1 and (v-1);
56    end;
57   end;
58  ans:=1<<60;
59  for i:=1 to n+1 do ans:=min(ans,dp[maxs,i]);
60  if n=1 then ans:=0;
61  writeln(ans);
62 
63 
64  close(input);
65  close(output);
66 end.

 O(3^n*n),预处理两个值

d[sta,i]      已取sta状态中的点到i点的最小值 预处理O(2^n*n^2)

f[x,y]      x状态中的点和y状态中的所有点连接最小长度之和=f[x,y-lowbit(y)]+d[x,z],z表示y中最后一个1的位置 预处理O(4^n) 需要保证x与y没有交集

 1 const max=500001;
 2 var dp,d:array[0..5000,0..20]of int64;
 3     dis:array[0..5000,0..5000]of int64;
 4     f:array[1..20,1..20]of int64;
 5     num:array[1..5000]of longint;
 6     n,m,i,j,x,y,z,k,maxs,v,v1:longint;
 7     s,ans:int64;
 8 
 9 function min(x,y:int64):int64;
10 begin
11  if x<y then exit(x);
12  exit(y);
13 end;
14 
15 function lowbit(x:longint):longint;
16 begin
17  exit(x and (-x));
18 end;
19 
20 begin
21  assign(input,'treasure.in'); reset(input);
22  assign(output,'treasure.out'); rewrite(output);
23  readln(n,m);
24  for i:=1 to n do
25   for j:=1 to n do f[i,j]:=1<<60;
26  for i:=1 to n do f[i,i]:=0;
27  for i:=1 to m do
28  begin
29   readln(x,y,z);
30   f[x,y]:=min(f[x,y],z);
31   f[y,x]:=min(f[y,x],z);
32  end;
33 
34  maxs:=(1<<n)-1;
35  for i:=1 to maxs do
36   for j:=1 to n do
37    if i and (1<<(j-1))=0 then
38    begin
39     d[i,j]:=1<<60;
40     for k:=1 to n do
41      if (j<>k)and(i and (1<<(k-1))>0) then d[i,j]:=min(d[i,j],f[j,k]);
42    end;
43 
44  m:=maxs;
45  for i:=1 to 12 do num[1<<(i-1)]:=i;
46  for i:=1 to maxs do
47   for j:=1 to maxs do dis[i,j]:=1<<60;
48  for i:=0 to maxs do dis[0,i]:=0;
49  for i:=0 to maxs do dis[i,0]:=0;
50 
51  for i:=1 to maxs do
52   for j:=1 to maxs do
53    if i and j=0 then
54    begin
55     x:=num[lowbit(j)];
56     dis[i,j]:=dis[i,j-lowbit(j)]+d[i,x];
57     if dis[i,j]>(1<<60) then dis[i,j]:=1<<60;
58    end;
59 
60  for i:=1 to maxs do
61   for j:=0 to n+1 do dp[i,j]:=1<<60;
62  for i:=1 to n do dp[1<<(i-1),0]:=0;
63  for i:=0 to n do
64   for j:=1 to maxs do
65   begin
66    v:=j xor m; v1:=v;
67    while v>0 do
68    begin
69     if dis[j,v]<1<<60 then
70      dp[j or v,i+1]:=min(dp[j or v,i+1],dp[j,i]+dis[j,v]*(i+1));
71     v:=v1 and (v-1);
72    end;
73   end;
74 
75  ans:=1<<60;
76  for i:=1 to n+1 do ans:=min(ans,dp[maxs,i]);
77  if n=1 then ans:=0;
78  writeln(ans);
79 
80 
81  close(input);
82  close(output);
83 end.
原文地址:https://www.cnblogs.com/myx12345/p/7986041.html