这题我是以连通域来做的,n个点至少需要n-1个边才能连通。如果形成回路,则相当于有这条边浪费了,没有任何价值。
最终至少还需要多少边才能把所有的城镇联通,也就是求剩下的孤立的点和连通域之间还需要几条边才能连通!比如:最后利用程序求得3个连通域和2个孤立的点,那么至少需要3+2-1=5条边才能全部连通;如果是两个连通域和1个孤立的点,则还至少需要2+1-1=2条边才能全部连通。
代码如下:
#include <stdio.h>
int main(){
int town[1000];
int temp,start,end;//start表示道路的起始城镇号,end表示另一端的城镇号;
int N,M;//N代表输入的城镇数,M代表输入的道路数;
int count1,count2,count3;//count1代表连通域的标号,count2代表连通域个数,count3代表独立的点数
while(scanf("%d",&N) != EOF&&N > 0){
//初始化
for(int i = 1; i <= N; i++){
town[i] = 0;
}
scanf("%d",&M);
count1 = 0;
count2 = 0;
count3 = N;
for(int i = 0; i < M; i++){
scanf("%d%d",&start,&end);
if(!town[start] && !town[end]){
town[start] = town[end] = ++count1;
count2++;
count3 = count3 - 2;
}else if(town[start] && !town[end]){
town[end] = town[start];
count3--;
}else if(!town[start] && town[end]){
town[start] = town[end];
count3--;
}else if(town[start] != town[end]){
/*for(int i = 1; i <= N; i++){ //这样是不对的!至于为什么,大家可以这么想,你用town[start]作为基准进行循环比较
//假设start=2,end=3,town[start]=1,town[end]=2,town[4]=2,town[5]=1;总共有N=5个城镇,
//则在进行循环时,当进行到自己和自己比较时,即if(town[2]==town[2])时,town[start]就被赋值成了town[end],
//即town[2]=town[3]=2,所以从start以后再进行比较时就都不对,因为比较的基准都发生变化了!!!
if(town[i] == town[start]){
town[i] = town[end];
}
}
count2--;
*/
temp=town[end];//必须用一个temp来保存比较基准,因为temp不会因比较而发生改变
for(int j=1;j<=N;j++){
if(town[j]==temp){town[j]=town[start];}
}
count2--;
}
}
printf("%d\n",count2 + count3 - 1);
}
return 0;
}
使用并查集,代码更简单易懂,如下:
#include <stdio.h>
int fin[1000];
int visit[1000];
int Find(int a){
if(a == fin[a]){
return a;
}
return Find(fin[a]);
}
void Merge(int a, int b){//两个连通域合并成一个连通域,将a连通域的根节点
//接到b连通域的根节点上!
a = Find(a);
b = Find(b);
fin[a] = fin[b];
}
int main(){
int a,b;
int N,M;
int j;
while(scanf("%d",&N) != EOF && N){
j = 0;
scanf("%d",&M);
for(int i = 1; i <= N; i++){
fin[i] = i;
visit[i] = 0;
}
while(M--){
scanf("%d %d",&a,&b);
Merge(a,b);
}
for(int i = 1; i <= N; i++){
visit[Find(i)] = 1;
}
for(int i = 1; i <= N; i++){
if(visit[i]) j++;//统计连通域个数(包括孤立节点)
}
printf("%d\n",j - 1);
}
}