图论:次小生成树
先求出MST。再枚举新加的一条边(u,v),这时成环便去掉(u,v)所在环上,即u到v的路径上的权值最大的边(不删(u,v)这个新增的边)。这样就是答案了
次小生成树一定至少有一条边与最小生成树不一样,那么存在不同于最小生成树的生成树中权值和最小的生成树就是次小生成树
处理出每对结点(u,v)的最小瓶颈路的最大边长f(u,v)。这样O(m log m)和O(n2)之后,就是由MST加一条边,删一条边(“边交换”)O(m)枚举m-n+1条加的边,最后O(1)算出新生成树的权值
总时间复杂度为O(m log m+n2+m)
然后介绍实现:
int n,m,cnt,mm; int fa[maxn],vis[maxn],vi[maxm],g[maxn]; int f[maxn][maxn]; struct Edge{int u,v,w,next;}e[maxm],ed[maxm];
fa是并查集的爹数组,vis是dfs的判重标记,vi是kruskal的判重标记
f是预处理出来的每对点的最小瓶颈路的最大边长
ed是生成树重新建图的数组,建图要双向边
int kruskal() { int tot=0,sum=0; for(int i=1;i<=n;i++) fa[i]=i; sort(e+1,e+m+1,cmp); for(int i=1;i<=m;i++) { int fx=find(e[i].u),fy=find(e[i].v); if(fx!=fy) { fa[fx]=fy;vi[i]=1; ed[++tot]=e[i];sum+=e[i].w; if(tot==n-1) break; } } mm=tot; return sum; }
int kruskal() { int tot=0,sum=0; for(int i=1;i<=n;i++) fa[i]=i; sort(e+1,e+m+1,cmp); for(int i=1;i<=m;i++) { int fx=find(e[i].u),fy=find(e[i].v); if(fx!=fy) { fa[fx]=fy;vi[i]=1; ed[++tot]=e[i];sum+=e[i].w; if(tot==n-1) break; } } mm=tot; return sum; }
求MST的过程顺便把路径记录下来
然后以最小生成树建树
void build() { memset(g,0,sizeof(g)); for(int i=1;i<n;i++) { int u=ed[i].u,v=ed[i].v; ed[i].next=g[u];g[u]=i; ed[++mm].u=v;ed[mm].v=u;ed[mm].w=ed[i].w; ed[mm].next=g[v];g[v]=mm; } }
然后DFS预处理出f数组
void dfs(int u) { vis[u]=1; for(int tmp=g[u];tmp;tmp=ed[tmp].next) { int v=ed[tmp].v; if(vis[v]) continue; for(int k=1;k<=n;k++) if(vis[k]) f[k][v]=f[v][k]=max(f[k][u],ed[tmp].w); dfs(v); } }
然后平扫一遍就好了
for(int i=1;i<=m;i++) { if(vi[i]) continue; mmn=min(mmn,mn-f[e[i].u][e[i].v]+e[i].w); //直接算出MST上删除一条边时的MST,即次小生成树 }
下面给出完整实现:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int INF=0x7f7f7f7f; 6 const int maxn=105; 7 const int maxm=5005; 8 int n,m,cnt,mm; 9 int fa[maxn],vis[maxn],vi[maxm],g[maxn]; 10 int f[maxn][maxn]; 11 struct Edge{int u,v,w,next;}e[maxm],ed[maxm]; 12 void addedge(int u,int v,int w) 13 { 14 e[++cnt].v=v;e[cnt].u=u;e[cnt].w=w; 15 //e[cnt].next=g[u];g[u]=cnt; 16 } 17 bool cmp(Edge x,Edge y) 18 { 19 return x.w<y.w; 20 } 21 int find(int x) 22 { 23 if(fa[x]!=x) fa[x]=find(fa[x]); 24 return fa[x]; 25 } 26 int kruskal() 27 { 28 int tot=0,sum=0; 29 for(int i=1;i<=n;i++) fa[i]=i; 30 sort(e+1,e+m+1,cmp); 31 for(int i=1;i<=m;i++) 32 { 33 int fx=find(e[i].u),fy=find(e[i].v); 34 if(fx!=fy) 35 { 36 fa[fx]=fy;vi[i]=1; 37 ed[++tot]=e[i];sum+=e[i].w; 38 if(tot==n-1) break; 39 } 40 } 41 mm=tot; 42 return sum; 43 } 44 void build() 45 { 46 memset(g,0,sizeof(g)); 47 for(int i=1;i<n;i++) 48 { 49 int u=ed[i].u,v=ed[i].v; 50 ed[i].next=g[u];g[u]=i; 51 52 ed[++mm].u=v;ed[mm].v=u;ed[mm].w=ed[i].w; 53 ed[mm].next=g[v];g[v]=mm; 54 } 55 } 56 void dfs(int u) 57 { 58 vis[u]=1; 59 for(int tmp=g[u];tmp;tmp=ed[tmp].next) 60 { 61 int v=ed[tmp].v; 62 if(vis[v]) continue; 63 for(int k=1;k<=n;k++) 64 if(vis[k]) f[k][v]=f[v][k]=max(f[k][u],ed[tmp].w); 65 dfs(v); 66 } 67 } 68 int main() 69 { 70 int T; 71 scanf("%d",&T); 72 int x,y,z; 73 while(T--) 74 { 75 cnt=0; 76 memset(vi,0,sizeof(vi)); 77 memset(f,0,sizeof(f)); 78 memset(vis,0,sizeof(vis)); 79 scanf("%d%d",&n,&m); 80 for(int i=1;i<=m;i++) 81 { 82 scanf("%d%d%d",&x,&y,&z); 83 addedge(x,y,z); 84 } 85 int mn=kruskal(),mmn=INF; 86 build();//最小生成树的边重新建树 87 dfs(1);//预处理两点间路径最大的边权 88 for(int i=1;i<=m;i++) 89 { 90 if(vi[i]) continue; 91 mmn=min(mmn,mn-f[e[i].u][e[i].v]+e[i].w); 92 //直接算出MST上删除一条边时的MST,即次小生成树 93 } 94 printf("%d %d\n",mn,mmn); 95 } 96 return 0; 97 }