P1029 沧溟探秘藏 官方题解
Gioush OJ · P1029 沧溟探秘藏
沧溟探秘藏
【题意简述】
给定 个点、 条边的带权无向图。可以任选起点,之后每次从已探索点扩展一个未探索点。若从深度为 的点经长度为 的边扩展,则代价为 。求探索所有点的最小总代价。
【Hint】
很小,可以用集合状态表示已经探索的点。由于代价与层数有关,转移时还需要枚举当前扩展发生在哪一层。
【提示】
设 表示已经探索集合为 ,当前准备扩展到第 层时的最小代价。
【解法】
先用 记录两点之间最短的直接边权。令 表示从已探索集合 向新增集合 扩展一层时的最小边权和,其中每个 都选择一条来自 的最小边。
设 表示完成若干层扩展后,已探索集合为 ,当前最大层数为 的最小代价。转移时枚举下一层新增集合 :
起点任意,因此所有单点集合初值为 。答案为全集状态的最小值。
【转移含义】
固定一棵探索树后,同一深度的结点可以任意交换探索顺序,费用只取决于它们从此前已探索集合连出的最小边。因此用新增集合 统一表示下一层, 取每个 到 的最小直接边权之和。
枚举 后, 正是完成一层扩展后的集合;乘上 正是这层边的深度系数。每一棵合法探索树按层划分都会产生一条转移序列,反过来每条转移序列也能取最小边连接为一棵合法探索树。
【复杂度】
状态数量为 ,枚举子集后复杂度约为 ,适合 的范围。
【参考代码】
/*Author:EhundateghDate:2026/7/21Name:treasure.cppYou steal,I kill.*/#include <vector>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;int n,m;int Val[13][13],In1,In2,In3,Pow[13]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096},B[4097];long long Dp[13][4097],Cost[13][4097],Ans=0x3f3f3f3f3f3f3f3f;int Lowbit(int x){return x&-x;}vector <int> Valid[4097];vector <long long> Pri[4097];int main(){ freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout); memset(Val,0x3f,sizeof(Val)); memset(Cost,0x3f,sizeof(Cost)); memset(Dp,0x3f,sizeof(Dp)); scanf("%d%d",&n,&m); int Temp=1; for(int i=1;i<=n;i++){ B[Temp]=i; Temp<<=1; } for(int i=1;i<=m;i++){ scanf("%d%d%d",&In1,&In2,&In3); Val[In1][In2]=min(Val[In1][In2],In3); Val[In2][In1]=min(Val[In2][In1],In3); } for(int i=0;i<(1<<n);i++){ for(int j=1;j<=n;j++){ if((i&(1<<(j-1)))==0) continue; for(int k=1;k<=n;k++){ Cost[k][i]=min(Cost[k][i],1ll*Val[k][j]); } } } for(int i=0;i<(1<<n);i++){ for(int j=(i-1)&i;;j=(j-1)&i){ int Diff=i-j; bool Tag=1; long long Sum=0; while(Diff){ if(Cost[B[Lowbit(Diff)]][j]>1e6){Tag=0; break;} Sum+=Cost[B[Lowbit(Diff)]][j]; Diff-=Lowbit(Diff); } if(Tag){Valid[i].push_back(j); Pri[i].push_back(Sum);} if(j==0) break; } } for (int i=1;i<=n;i++) { Dp[1][1<<(i-1)]=0; } for(int i=1;i<=n;i++){ for(int j=0;j<=(1<<n)-1;j++){ for(int k=0;k<Valid[j].size();k++){ Dp[i][j]=min(Dp[i][j],Dp[i-1][Valid[j][k]]+(i-1)*Pri[j][k]); } } Ans=min(Ans,Dp[i][(1<<n)-1]); } printf("%lld\n",Ans); return 0;}