题目大意:
给定一个无向图,寻找它的最小生成树,如果仅有一种最小生成树,输出所有边的和,否则输出unique!
根据kruscal原理来说,每次不断取尽可能小的边不断添加入最小生成树中,那么可知如果所有边的长度都不相同,那么kruscal取得过程必然只有一种情况,由小到大
所以要是存在多种情况的最小生成树,那么必然是存在相同的边
初始将所有相同的边进行标记,生成第一次最小生成树后,不断去除其中带标记的边,然后再计算最小生成树,判断能否得到同样的答案,如果可以,说明不止一种情况
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 #define N 105 6 int fa[N] , same[N] , first[N] , k; 7 int rec[N] , amo;//rec[]记录MST中含有相同长度边的位置,amo记录其数量 8 struct Edge{ 9 int x,y,d,next,flag; 10 bool same; 11 bool operator<(const Edge &m) const{ 12 return d<m.d; 13 } 14 }e[N*N]; 15 16 int find_head(int x) 17 { 18 while(fa[x]!=x) x=fa[x]; 19 return x; 20 } 21 22 bool Union(int x,int y) 23 { 24 int fa_x = find_head(x); 25 int fa_y = find_head(y); 26 fa[fa_x] = fa_y; 27 return fa_x == fa_y; 28 } 29 30 void add_edge(int x, int y , int d) 31 { 32 e[k].x=x , e[k].y=y , e[k].d=d , e[k].flag=1 , e[k].next=first[x]; 33 e[k].same = false; 34 first[x] = k++; 35 } 36 37 int cal_MST(int n , int flag) 38 { 39 int ans = 0 , cnt=0; 40 for(int i=1 ; i<=n ; i++) fa[i]=i; 41 for(int i=0 ; i<k ; i++){ 42 if(e[i].flag==1){ 43 if(!Union(e[i].x , e[i].y)){ 44 ans+=e[i].d; 45 if(e[i].same && flag){ 46 rec[amo++] = i; 47 } 48 cnt++; 49 if(cnt == n-1) break; 50 } 51 } 52 } 53 return ans; 54 } 55 56 int main() 57 { 58 int T; 59 scanf("%d" , &T); 60 while(T--) 61 { 62 int n , m , x , y , d; 63 scanf("%d%d" , &n , &m); 64 k=0; 65 memset(first , -1 , sizeof(first)); 66 for(int i=0 ; i<m ; i++){ 67 scanf("%d%d%d" , &x , &y , &d); 68 add_edge(x , y , d); 69 } 70 71 sort(e , e+k); 72 //对存在相同边的边进行标记 73 for(int i=1 ; i<k ; i++) 74 if(e[i].d == e[i-1].d) e[i].same=e[i-1].same=true; 75 amo = 0; 76 int ans = cal_MST(n , 1); 77 bool is_unique = true; 78 for(int i=0 ; i<amo ; i++){ 79 e[rec[i]].flag = 0; 80 int t=cal_MST(n , 0); 81 if(t == ans){ 82 is_unique=false; 83 break; 84 } 85 e[rec[i]].flag = 1; 86 } 87 if(is_unique) printf("%d\n" , ans); 88 else puts("Not Unique!"); 89 } 90 return 0; 91 }
上面那个明显复杂度比较高
我们可以求解出次小生成树的值与最小生成树的值进行比较判断是否唯一
先求出最小生成树,用二维数组mx[][]记录最小生成树上两个点之间路径上最长边的长度
然后找到每一条不属于最小生成树的边u,v ,这样可以与原最小生成树中u->v的路径形成一个环,那么最后需要在环中删去一条最长边,那么只要不断得到这个差值的最小值
用最小生成树的值减去他就可以了
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 #define N 105 6 const int INF = 0x3f3f3f3f; 7 int mx[N][N] , w[N][N]; 8 int n , m; 9 int d[N] , connect[N]; 10 bool vis[N][N] , in[N]; 11 12 int prim() 13 { 14 int ret = 0; 15 memset(vis , 0 , sizeof(vis)); 16 memset(connect , 0 , sizeof(connect)); 17 memset(in , 0 , sizeof(in)); 18 memset(mx , 0 , sizeof(mx)); 19 d[1] = INF , in[1] = true; 20 for(int i=2 ; i<=n ; i++) 21 if(w[1][i]>=0){ 22 d[i] = w[1][i]; 23 connect[i] = 1; 24 } 25 else d[i] = INF; 26 27 for(int i=1 ; i<n ; i++){ 28 int minn = INF , index = 0; 29 for(int j=1 ; j<=n ; j++){ 30 if(in[j]) continue; 31 if(d[j]<minn) minn=d[j] , index=j; 32 } 33 int u = connect[index]; 34 d[index] = INF , vis[index][u] = vis[u][index] = true; 35 mx[index][u] = mx[u][index] = minn , in[index] = true , ret+=minn; 36 for(int j=1 ; j<=n ; j++){ 37 if(in[j] || w[index][j]<0) continue; 38 if(w[index][j]<d[j]) d[j] = w[index][j] , connect[j] = index; 39 } 40 for(int j=1 ; j<=n ; j++){ 41 if(!in[j]) continue; 42 mx[j][index] = mx[index][j] = max(mx[index][j] , max(mx[index][u] , minn)); 43 } 44 } 45 return ret; 46 } 47 48 int sec_mst(int mst) 49 { 50 int del = INF; 51 for(int i=1 ; i<=n ; i++){ 52 for(int j=i+1 ; j<=n ; j++){ 53 if(!vis[i][j] && w[i][j]>=0){ 54 del = min(del , mx[i][j]-w[i][j]); 55 } 56 } 57 } 58 return mst-del; 59 } 60 61 int main() 62 { 63 // freopen("in.txt" , "r" , stdin); 64 int T; 65 scanf("%d" , &T); 66 while(T--) 67 { 68 scanf("%d%d" , &n , &m); 69 memset(w , -1 , sizeof(w)); 70 int u , v , wei; 71 while(m--){ 72 scanf("%d%d%d" , &u , &v , &wei); 73 w[u][v] = w[v][u] = wei; 74 } 75 int ret = prim(); 76 int sec = sec_mst(ret); 77 if(ret == sec) puts("Not Unique!"); 78 else printf("%d\n" , ret); 79 } 80 return 0; 81 }
我还在坚持,我还未达到我所想,梦~~一直在