题意:求解最小生成树的权值是否唯一,即要我们求次小生成树的权值
两种方法求最小生成树,一种用prim算法, 一种用kruskal算法
一:用prim算法
对于给定的图,我们可以证明,次小生成树可以由最小生成树变换一边得到。 那么我们可以如下求给定图的次小生成树。首先,我们用prime算法求出图的最小生成树, 在这个过程中记录每条边是否用过,以及两个点之间最短路径上的最大权值F[i,j]
F[i,j]可以如此求得,当加入点u的时候,并且u的父结点是v 那么对于已经在生成树中的节点x F[x,u] = max(F[x,v],w[u][v]),那么我么就可以用Prime算法一样的时间复杂度来求出图的次小生成树。
参考链接:http://blog.csdn.net/lyg_wangyushi/article/details/4371734
#include <iostream> #include <algorithm> #include <string.h> #include <stdio.h> #include <string> #include <queue> using namespace std; const int INF=0x3f3f3f3f; int n,m; int ans1,ans2; int w[101][101]; int dis[101]; int pre[101]; int vis[101]; int use[101][101]; //use[i][j]=1表示边(i,j)在最小生成树里,=0则不在 int f[101][101]; //f[u][v]表示结点u到结点v的最短路径上边的最大值(即最大边的值) vector<int> son[101]; //son[i]存储的是与i连接的端点 void init() { memset(pre,0,sizeof(pre)); memset(dis,INF,sizeof(dis)); memset(vis,0,sizeof(vis)); memset(f,0,sizeof(f)); memset(use,0,sizeof(use)); } //求最小生成树 int solve_MST() { init(); vector<int> node; int s=1,counts=0,ans=0,tmp,k; dis[s]=0; pre[s]=s; node.push_back(s); //一开始dis都赋值为INF,所以为了减少一点点遍历的时间,node存储的是dis不为INF的点 while(1) { tmp=INF; for(int i=0; i<node.size(); i++) { int v=node[i]; if(!vis[v]&& dis[v]<tmp) { tmp=dis[v]; k=v; //k即为在没有进入最小生成树的点中到树的距离(dis[k])最小的点。 } } if(tmp==INF) break; use[k][pre[k]]=use[pre[k]][k]=1; for(int i=1;i<=n;i++){ if(vis[i]){ f[i][k]=f[k][i]=max(f[i][pre[k]],w[k][pre[k]]); f[i][k]=f[k][i]=max(f[pre[k]][i],w[k][pre[k]]); } } ans+=tmp; vis[k]=1; for(int i=0; i<son[k].size(); i++) { int v=son[k][i]; if(!vis[v] && w[k][v]<dis[v]) { dis[v]=w[k][v]; pre[v]=k; node.push_back(v); } } } return ans; } //求次小生成树 int second_MST(int ans){ int second=INF; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(!use[i][j] && ans-f[i][j]+w[i][j]<second){ second=ans-f[i][j]+w[i][j]; } } } return second; } int main() { int t,a,b,c; scanf("%d",&t); for(int i=1;i<=t;i++){ scanf("%d%d",&n,&m); memset(w,INF,sizeof(w)); for(int i=0;i<101;i++){ son[i].clear(); } for(int j=1;j<=m;j++){ scanf("%d%d%d",&a,&b,&c); w[a][b]=w[b][a]=c; son[a].push_back(b); son[b].push_back(a); } ans1=solve_MST(); ans2=second_MST(ans1); if(ans1==ans2) printf("Not Unique! "); else printf("%d ",ans1); } return 0; }
二:用kruskal算法
枚举删除最小生成树上的边,再求最小生成树,即总共求n-1次最小生成树,取其中最小值。
这道题如果用该方法,有一点要注意,不然的话会一直WA。
我做这道题的时候一直wrong answer的原因是因为, 可能求出的次小生成树正好等于最小生成树的总权值,但是它不连通,即边的个数小于n-1
可以试试这个测试数据:
1
6 7
1 3 1
1 2 2
2 3 3
3 4 0
4 5 4
4 6 5
5 6 6
结果是 12
就是说,如果次小生成树不连通,且最小生成树中被你删的边恰好是权值为0的情况, 这样权值总和仍相等,但不满足生成树的要求。
#include <iostream> #include <algorithm> #include <string.h> #include <stdio.h> #include <string> using namespace std; int t,n,m,cost,x,y,w; //n个节点,m条边 int ansMST,secondMST; int numOfEdge; struct Edge{ int u,v; int cost; bool operator < (const Edge& tmp) const { return cost< tmp.cost; } }edge[5500],MSTedge[210]; struct UF{ int father[110]; void unit(){ for(int i=1;i<=n;i++){ father[i]=i; } } int find_root(int x){ if(father[x]!=x) father[x]=find_root(father[x]); return father[x]; } void Union(int fa,int fb){ father[fb]=fa; } }uf; //求最小生成树 int solveMST(){ int counts=0; int ans=0; //int index=0; for(int i=0;i<m;i++){ int u=edge[i].u; int v=edge[i].v; int fu=uf.find_root(u); int fv=uf.find_root(v); if(counts>=n-1){ break; } if(fu!=fv){ ans+=edge[i].cost; uf.Union(fu,fv); MSTedge[counts].u=u; MSTedge[counts].v=v; MSTedge[counts].cost=edge[i].cost; counts++; } } return ans; } /** a、b表示最小生成树中删去的那条边 这里是删去(a,b)边后,求最小生成树 */ int solve(int a,int b){ int counts=0; int ans=0; for(int i=0;i<m;i++){ int u=edge[i].u; int v=edge[i].v; int fu=uf.find_root(u); int fv=uf.find_root(v); if((u==a && v==b)||(u==b && v==a)) continue; if(counts>=n-1){ break; } if(fu!=fv){ ans+=edge[i].cost; counts++; uf.Union(fu,fv); } } numOfEdge=counts; return ans; } int main() { scanf("%d",&t); for(int i=1;i<=t;i++){ scanf("%d%d",&n,&m); for(int j=0;j<m;j++){ scanf("%d%d%d",&x,&y,&w); edge[j].u=x; edge[j].v=y; edge[j].cost=w; } sort(edge,edge+m); uf.unit(); ansMST=solveMST(); //最小生成树的总权值 secondMST=100000000; //次小生成树的总权值 //枚举,取最小值 for(int j=0;j<n-1;j++){ int u=MSTedge[j].u; int v=MSTedge[j].v; uf.unit(); int total=solve(u,v); //如果它的总权值小于目前的secondMST,并且边的个数也正好为n-1 if(total<secondMST && numOfEdge==n-1){ secondMST=total; } } if(secondMST==ansMST) printf("Not Unique! "); else printf("%d ",ansMST); } return 0; }