题意:求次小生成树,如果存在次小生成树则输出“Not Unique!”,否则输出最小生成树的值。
分析:次小生成树,可以用prim求出最小生成树,再枚举删除最小生成树中的每条边求最小生成树。也可用Kruskal做。这里要注意几种特殊情况1.图不联通2.不存在次小生成树3.有重边。
次小生成树学习见这里:http://www.cppblog.com/MatoNo1/archive/2011/05/29/147627.aspx
#include<cstdio> #include<vector> #include<cstring> #include<algorithm> #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) using namespace std; const int maxn=110; const int inf=0x3f3f3f3f; int low[maxn],father[maxn],map[maxn][maxn],dp[maxn][maxn]; bool visit[maxn],use[maxn][maxn]; //father[i]记录i的父亲结点,dp[i][j]记录最小生成树中结点i到j的最大路径长度,use[i][j]记录边(i,j)是否在最小生成树中 int prim(int n){ int ans=0,tmp=inf,idx; for(int i=1;i<=n;i++){ //初始化各数组值 visit[i]=false; low[i]=map[1][i]; father[i]=1; } memset(use,false,sizeof(use)); memset(dp,0,sizeof(dp)); low[1]=0; visit[1]=true; vector<int>inq; //inq用于记录已在最小生成树中的结点 inq.push_back(1); for(int k=1;k<n;k++){ tmp=inf; for(int i=1;i<=n;i++){ if(!visit[i]&&low[i]<tmp){ tmp=low[i];idx=i; } } for(int i=0;i<inq.size();i++){ //每找到一个结点,就更新dp[][]的值 dp[inq[i]][idx]=dp[idx][inq[i]]=max(dp[inq[i]][father[idx]],low[idx]); } ans+=tmp; //如果tmp==inf,则说明找不到一条边使图连通,则原图不连通,这题不存在不连通的情况 visit[idx]=true; inq.push_back(idx); use[father[idx]][idx]=use[idx][father[idx]]=true; for(int i=1;i<=n;i++){ if(!visit[i]&&low[i]>map[i][idx]){ low[i]=map[i][idx]; father[i]=idx; //在更新low[]时更新father[] } } } return ans; } bool S_MST(int n){ //求次小生成树 int flg=inf; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(!use[i][j]&&map[i][j]!=inf) flg=min(flg,map[i][j]-dp[i][j]); } } return flg==0?true:false; } int main() { int cas; scanf("%d",&cas); while(cas--){ int n,m; scanf("%d %d",&n,&m); memset(map,inf,sizeof(map)); for(int i=0;i<m;i++){ int u,v,w; scanf("%d %d %d",&u,&v,&w); map[u][v]=map[v][u]=w; } int ans=prim(n); if(S_MST(n)) printf("Not Unique!\n"); else printf("%d\n",ans); } return 0; }
此题用kruskal算法解的代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=110; struct node{ int u,v,w; }Edge[maxn*maxn]; int father[maxn],used[maxn]; int n,m,cost1,cost2,num; bool flag1,flag2; bool cmp(node a,node b){ return a.w<b.w; } void make_set() { for(int i=1;i<=n;i++) father[i]=i; } int find(int x) { if(x!=father[x]) father[x]=find(father[x]); return father[x]; } void kruskal() { num=0;cost1=0; if(m<n-1){ //如果图不连通,标记flag1=true,返回 flag1=true; return ; } make_set(); sort(Edge,Edge+m,cmp); for(int i=0;i<m&&num<n-1;i++){ int x=find(Edge[i].u); int y=find(Edge[i].v); if(x!=y){ father[x]=y; used[++num]=i; cost1+=Edge[i].w; } } if(num!=n-1){ //如果图不连通,标记flag1=true,返回 flag1=true; cost1=0; } } void sec_kruskal() { int tot; for(int i=1;i<=num;i++){ tot=0;cost2=0; make_set(); for(int j=0;j<m&&tot<n-1;j++){ if(j==used[i]) continue; int x=find(Edge[j].u); int y=find(Edge[j].v); if(x!=y){ father[x]=y; tot++; cost2+=Edge[j].w; } } if(tot==n-1&&cost2==cost1){ flag2=true; return ; } } } int main() { int cas; scanf("%d",&cas); while(cas--){ scanf("%d %d",&n,&m); for(int i=0;i<m;i++) scanf("%d %d %d",&Edge[i].u,&Edge[i].v,&Edge[i].w); cost1=cost2=0; flag1=flag2=false; kruskal(); if(flag1) { printf("0\n"); continue; } if(m==n-1) { //图连通且仅有n-1条边,直接输出 printf("%d\n",cost1); continue; } sec_kruskal(); if(flag2) printf("Not Unique!\n"); else printf("%d\n",cost1); } return 0; }