kruskal,第一遍保存重复权值的边,然后把每一条保存的删掉一次做kruskal,这道题代码有好几个亮点重点降低复杂度,首先是路径压缩新方法,再是qsort对结构体排序;
答案:
#include<stdio.h>
#include<stdlib.h>
#define inf 9999//kruskal,第一遍保存收集的边,然后把每一条保存的删掉一次kruskal
//若删掉了原树的一条还可以有同样权值的树就说明不唯一
//但是每条都试试会超时
//其实只有相同权重的边有可能,因为一颗最小生成树边一定是nv-1,且从小到大收
//所以只col中收集重复权重的边 (注意如果e1和e2重复,则两条都要收集)
typedef struct et{
int v1,v2;
int val;
}et;
et e[130000]; //这里边数很大,99999不够
int nv,ne,par[99999],col[130000],cnt,weight,cs;
/*int Find(int a){
int i;
for(i=a;par[i]!=i;i=par[i]);
par[a]=i;//顺便路径压缩以下
return i;
}*///这种路径压缩还是不够,还是超时换下面这种,很重要
int Find(int a){
return a==par[a]?a:(par[a]=Find(par[a]));
}
int cmp(const void* a,const void* b){
return (*(const struct et*)a).val-(*(const struct et*)b).val;
//注意.优先级高,解引用前要加一个括号
}
int main(){
et p;
int msts=0,i,j,count=0,w2;
scanf("%d%d",&nv,&ne);
for(i=0;i<ne;i++){
scanf("%d%d%d",&e[i].v1,&e[i].v2,&e[i].val);
}
for(i=1;i<=nv;i++){
par[i]=i;
}
/* for(i=0;i<ne;i++){
for(j=0;j<ne-i-1;j++){
if(e[j].val>e[j+1].val){
p=e[j];
e[j]=e[j+1];
e[j+1]=p;
}
}
}*///冒泡排序时间复杂度太高
qsort(e,ne,sizeof(et),cmp);
for(i=0;i<ne;i++){
if(Find(e[i].v1)!=Find(e[i].v2)){//不成环
// par[e[i].v1]=Find(e[i].v2);
par[Find(e[i].v1)]=Find(e[i].v2);
//这里很重要,应该让v1所在根的par等于v2所在根!
msts++;
weight+=e[i].val;
if(i<nv-1&&e[i].val==e[i+1].val||i&&e[i].val==e[i-1].val) col[cs++]=i;
// printf("%dkk ",i);
if(cs==nv-1) break;
}
}
// printf("\n");
for(i=1;i<=nv;i++){
if(Find(i)==i) count++;
}
if(count!=1) printf("No MST\n%d",count);
else if(cs==0) printf("%d\nYes",weight);
else{
printf("%d\n",weight);
int f=0;
for(j=0;j<cs;j++){
w2=0;
cnt=0;
for(i=1;i<=nv;i++){
par[i]=i;
}
for(i=0;i<ne;i++){
if(i==col[j]) continue;//每条边都舍弃试试
if(Find(e[i].v1)!=Find(e[i].v2)){//不成环
par[Find(e[i].v1)]=Find(e[i].v2);
cnt++;
w2+=e[i].val;
if(cnt==nv-1) break;
// printf("%d %dpp ",i,w2);
}
}
if(w2==weight&&cnt==nv-1){
f=1;
break;
}
}
if(f) printf("No");
else printf("Yes");
}
return 0;
}
路径压缩改良:
/*int Find(int a){
int i;
for(i=a;par[i]!=i;i=par[i]);
par[a]=i;//顺便路径压缩以下
return i;
}*///这种路径压缩还是不够,还是超时换下面这种,很重要
int Find(int a){
return a==par[a]?a:(par[a]=Find(par[a]));
}
qsort对结构体排序替代冒泡排序:
/* for(i=0;i<ne;i++){
for(j=0;j<ne-i-1;j++){
if(e[j].val>e[j+1].val){
p=e[j];
e[j]=e[j+1];
e[j+1]=p;
}
}
}*///冒泡排序时间复杂度太高
int cmp(const void* a,const void* b){
return (*(const struct et*)a).val-(*(const struct et*)b).val;
//注意.优先级高,解引用前要加一个括号
}
qsort(e,ne,sizeof(et),cmp);
题目:
给定一个带权无向图,如果是连通图,则至少存在一棵最小生成树,有时最小生成树并不唯一。本题就要求你计算最小生成树的总权重,并且判断其是否唯一。
输入格式:
首先第一行给出两个整数:无向图中顶点数 N(≤500)和边数 M。随后 M 行,每行给出一条边的两个端点和权重,格式为“顶点1 顶点2 权重”,其中顶点从 1 到N 编号,权重为正整数。题目保证最小生成树的总权重不会超过 230。
输出格式:
如果存在最小生成树,首先在第一行输出其总权重,第二行输出“Yes”,如果此树唯一,否则输出“No”。如果树不存在,则首先在第一行输出“No MST”,第二行输出图的连通集个数。
输入样例 1:
5 7
1 2 6
5 1 1
2 3 4
3 4 3
4 1 7
2 4 2
4 5 5
输出样例 1:
11
Yes
输入样例 2:
4 5
1 2 1
2 3 1
3 4 2
4 1 2
3 1 3
输出样例 2:
4
No
输入样例 3:
5 5
1 2 1
2 3 1
3 4 2
4 1 2
3 1 3
输出样例 3:
No MST
2