解法参考的论文:https://wenku.baidu.com/view/8abefb175f0e7cd1842536aa.html
觉得网上的代码好像都是用邻接矩阵来实现的,觉得可能数据量大了会比较慢。于是自己写了一遍。
实现细节可以看代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<vector> using namespace std; const int N=10000+10; const int INF=0x3f3f3f3f; int n,m,k,ans,fa[N]; struct edge{ int x,y,z; bool used; edge() {} edge(int x,int y,int z) : x(x),y(y),z(z) { used=0; } bool operator < (const edge &rhs) const { return z<rhs.z; } }e[N]; map<string,int> mp; vector<int> G[N]; int getfa(int x) { return x==fa[x] ? x : fa[x]=getfa(fa[x]); } void kruskal() { sort(e+1,e+m+1); for (int i=1;i<=n;i++) fa[i]=i; for (int i=1;i<=m;i++) { int fx=getfa(e[i].x),fy=getfa(e[i].y); if (e[i].x==1 || e[i].y==1 || fx==fy) continue; fa[fy]=fa[fx]; e[i].used=1; ans+=e[i].z; } } int Max[N]; void dfs(int x,int f) { for (int i=0;i<G[x].size();i++) { int y; if (e[G[x][i]].x==x) y=e[G[x][i]].y; else y=e[G[x][i]].x; if (!e[G[x][i]].used || y==f) continue; if (x!=1) //这个很重要:去掉的边必须和V0不相连(否则会使得根节点度数减少) if (Max[x]==0 || e[G[x][i]].z>e[Max[x]].z) Max[y]=G[x][i]; else Max[y]=Max[x]; dfs(y,x); } } int cnt=0,Min[N]; void solve() { memset(Min,0,sizeof(Min)); for (int i=0;i<G[1].size();i++) { //把各个MST森林最小边连到根节点 变成一棵树 int ty; if (e[G[1][i]].x==1) ty=getfa(e[G[1][i]].y); else ty=getfa(e[G[1][i]].x); if (Min[ty]==0 || e[G[1][i]].z<e[Min[ty]].z) Min[ty]=G[1][i]; } for (int i=1;i<=n;i++) if (Min[i]) { cnt++; ans+=e[Min[i]].z; e[Min[i]].used=1; } for (int i=cnt+1;i<=k;i++) { //拓展根节点度数 memset(Max,0,sizeof(Max)); dfs(1,0); //dfs找每个点到根节点的路径上的最长边 int minn=INF,New,Old; for (int j=0;j<G[1].size();j++) if (!e[G[1][j]].used) { int y; if (e[G[1][j]].x==1) y=e[G[1][j]].y; else y=e[G[1][j]].x; if (Max[y]==0) continue; int tmp=e[G[1][j]].z-e[Max[y]].z; if (tmp<minn) { //记录本次结点拓展最大的收益 minn=tmp; New=G[1][j]; Old=Max[y]; } } if (minn>=0) break; ans+=minn; e[New].used=1; e[Old].used=0; //替换最长边 } } int main() { cin>>m; string s1,s2; int x,y,z; n=1; mp["Park"]=1; for (int i=1;i<=m;i++) { cin>>s1>>s2>>z; if (!mp.count(s1)) mp[s1]=++n; if (!mp.count(s2)) mp[s2]=++n; x=mp[s1]; y=mp[s2]; e[i]=edge(x,y,z); } cin>>k; kruskal(); //先忽略根节点做一次MST 得到MST森林 for (int i=1;i<=m;i++) { G[e[i].x].push_back(i); G[e[i].y].push_back(i); } solve(); //拓展根节点度数得到更小的MST printf("Total miles driven: %d ",ans); return 0; }