数据结构之最小生成树

121 阅读1分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第1篇笔记

题目大意:

选择以一个点作为起点,求这点点到其他点的值最小,起点到下一个点的值为,i点到起点集合的距离*集合点的个数。

思路:

总体:这题是求起点到其他所有点的”特殊权值“最小,就要用最小生成树来做,但是还要求点是否在集合中所以又要用状压dp更新所有点,即用最小生成树的模板加上状压dp判断状态。

  1. 枚举1~1<
for(int i=1;i< 1<<n ;i++){ for(int j=0;j<n;j++){ if(i>>j & 1){//这一位有数 for(int u=0;u<n;u++){ if(g[j][u]!=0x3f3f3f3f){ d[i]|=1<<u; } } } } }
  1. 枚举所有状态的子集,即可以到i的所有状态,选出一步就可以到的状态j
  2. 判断j比i少几个点,并且少的路径总权值是多少。
  3. 用j状态更新i状态,更新每个点的状态,即j可以在任意一层到i。
memset(f,0x3f,sizeof(f)); for(int i=0;i<n;i++)f[1<<i][0]=0;//开始送一个点 for(int i=1 ;i< 1<<n;i++){ for(int j=(i-1)&i;j;j=(j-1)&i){ if((d[j]&i)==i){//要加括号,&优先级很低 int res=0,temp=i^j; for(int k=0;k<n;k++){ if(temp>>k & 1){ int t=0x3f3f3f3f; for(int u=0;u<n;u++){ if(j>>u & 1)t=min(g[k][u],t); } res+=t; } } for(int k=1;k<n;k++){ f[i][k]=min(f[i][k],f[j][k-1]+res*k); } } } }

代码:

#include<bits/stdc++.h> using namespace std; int g[1010][1010]; int d[1<<12]; int f[1<<12][12]; int main(){ int n,m,u,v,w; cin>>n>>m; memset(g,0x3f,sizeof(g)); for(int i=0;i<n;i++)g[i][i]=0; for(int i=0;i<m;i++){ cin>>u>>v>>w; u--; v--; g[u][v]=g[v][u]=min(g[u][v],w); } for(int i=1;i< 1<<n ;i++){ for(int j=0;j<n;j++){ if(i>>j & 1){//这一位有数 for(int u=0;u<n;u++){ if(g[j][u]!=0x3f3f3f3f){ d[i]|=1<<u; } } } } } memset(f,0x3f,sizeof(f)); for(int i=0;i<n;i++)f[1<<i][0]=0;//开始送一个点 for(int i=1 ;i< 1<<n;i++){ for(int j=(i-1)&i;j;j=(j-1)&i){ if((d[j]&i)==i){//要加括号,&优先级很低 int res=0,temp=i^j; for(int k=0;k<n;k++){ if(temp>>k & 1){ int t=0x3f3f3f3f; for(int u=0;u<n;u++){ if(j>>u & 1)t=min(g[k][u],t); } res+=t; } } for(int k=1;k<n;k++){ f[i][k]=min(f[i][k],f[j][k-1]+res*k); } } } } int ans=0x3f3f3f3f; for(int i=0;i<n;i++)ans=min(f[(1<<n)-1][i],ans); cout<<ans<<endl; }

当时错因:

学习心得:

复习心得: