一、找出一条路,可以连接所有顶点,并且路径之和最小!《一个连通的无向图,且没有回路,那么这就是求一颗最小生成树》
思路:
1.要想总长度之和最小,那么应该从最短的边开始选取,将边进行由小到大排序;
2.从最小边开始选,并且要保证不能有回路,按照并查集算法,即判断两个点是否在同一个集合(有相同的父节点);
3.递归,直到选出 N-1 条边为止(无回路的 n 个点连接,只需要 n-1 条边)
struct edge{ // u = 起点,v = 终点, w = 边长
int u;
int v;
int w;
};
struct edge e[10]; //用于存储输入边
int n,m;
int f[7]={0},sum=0,count=0;
void quicksort(int left, int right){ // 第一章学习的快速排序算法,时间复杂度O(logN)
int i,j;
struct edge t;
if(left>right)
return;
i=left;
j=right;
while(i!=j){
// 以left为基准,查找的顺序很重要,先从右边开始搜索,然后才是左边
while(e[j].w>=e[left].w && i<j)
j--;
while(e[i].w<=e[left].w && i<j)
i++;
if(i<j){ // 比left大的都放右边,比left小的都放左边
t=e[i];
e[i]=e[j];
e[j]=t;
}
}
// 基准点left,已经被调到 i
t=e[left];
e[left]=e[i];
e[i]=t;
// 递归左右两边的队列,完成 由小到大排序
quicksort(left,i-1);
quicksort(i+1,right);
return;
}
int getf(int v){
if(f[v]==v)
return v;
else{
f[v]=getf(f[v]);
return f[v];
}
}
int merge(int x, int y){
int u,v;
u=getf(x);
v=getf(y);
// 如果两个点不在同一集合中,合并成功;否则,合并失败
if(u!=v){
f[v]=u;
return 1;
}
return 0;
}
int main(){
int i;
scanf("%d %d",&n,&m);
for(i=1;i<=m;i++)
scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
quicksort(1,m);
for(i=1;i<=n;i++)
f[i]=i;
for(i=1;i<=m;i++){
if(merge(e[i].u,e[i].v)){ // 如果合并成功
count++;
sum=sum+e[i].w;
}
if(count==m-1)
break;
}
printf("%d ",sum);
getchar();getchar();return 0;
}
二、关于 quicksort 的一点DEBUG:
1.首先界定了 left > right 时,quicksort结束,那么什么时候 left 会大于 right 呢?
2.在下面这个地方:首先右边的数大于等于left时,指针可以左移 j--;然后左边的数小于"等于"left时,指针也会右移;就出现了刚好 e[i]=e[j]=e[left]的一个位置,这时候j--,i++,就出现了 i > j,left交换的位置 i,成立的不等式为 e[i] >= e[j].
while(i!=j){
while(e[j].w>=e[left].w && i<j)
j--;
while(e[i].w<=e[left].w && i<j)
i++;
一定要注意好边界!