首先想到的是最标准的并查集
但是WA了,因为图有环,导致一个环内有2个father节点(只被他人指向而不指向他人,导致的结果)
···思索到最后····
最后看来其实这也对了,依然是union函数错了(注释掉的部分),改掉之后AC了
fat[x]=find(fat[y]);
union中注释掉的部分这样的写法就会导致fat没有连接到最高
比如1-5,3-5,1-9,3-9
这样会导致fat[1]=fat[3]=9,fat[5]=5,fat[9]=9,
改进后,就会在1-9时,fat[find(1)]=fat[5]=9,这样就能连接起来了
fat[find(x)]=find(y);
#include<bits/stdc++.h>
using namespace std;
/*
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。
省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通
(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。
问最少还需要建设多少条道路?
*/
vector<int>fat(1001,0);
void init(int n)
{
for(int i=1;i<=n;i++)
{
fat[i]=i;
}
}
int find(int x)
{
if(x==fat[x]){return fat[x];}
return find(fat[x]);
}
void uni(int x, int y)
{
//fat[x]=find(fat[y]);
fat[find(x)]=find(y);
}
int main(){
//要用并查集
int n,m;
while(cin>>n>>m)
{ int x,y;
set<int>s;
init(n);
vector<int>v(n+1,0);
for(int i=0;i<m;i++)
{ cin>>x>>y;
if(x==0||y==0){break;}
uni(x,y);//并查集
uni(y,x);
v[x]=1;v[y]=1;//将出现过的节点保存
}
//出现过的节点的父亲放入set,最后看set的size即可知道有几个连通图,还差几个
//比如4个节点,有2个连通图,且包含了3个节点
//那么把两个连通图连起来一条边,把第四个节点加入再一条边
int num=count(v.begin(),v.end(),1);
for(int i=1;i<=n;i++)
{
if(v[i]==1)//访问过,就判断属于哪个连通子图
{
s.insert(find(i));
}
}
int num_lt=s.size();
auto it=s.begin();
cout<<fat[5]<<" "<<fat[6]<<fat[9]<<" "<<endl;
cout<<n<<" "<<num<<" "<<num_lt<<endl;
cout<<n-num+num_lt-1<<endl;
}
return 0;
}
改进:在father数组中,加上一个height(太麻烦了)
最后发现,其实只要去掉多此一举的init(之前看的并查集都是先把自己立为自己的father),就不会因为环导致一直指向自己
但是经过测试,这依然不对,因为这会导致所有人的fat都是0
(如输入uni(1,2),因为没有初始化,所以fat[2]=0,find(0)=0,fat[1]=0,这显然不对)
void uni(int x, int y)
{
fat[x]=find(fat[y]);
}
这是罪魁祸首,其实写反了
#include<bits/stdc++.h>
using namespace std;
/*
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。
省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通
(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。
问最少还需要建设多少条道路?
*/
vector<int>fat(1001,0);
void init(int n)
{
for(int i=1;i<=n;i++)
{
fat[i]=i;
}
}
int find(int x)
{
if(x==fat[x]){return fat[x];}
return find(fat[x]);
}
void uni(int x, int y)
{
fat[x]=find(fat[y]);
}
int main(){
//要用并查集
int n,m;
while(cin>>n>>m)
{ int x,y;
set<int>s;
//init(n);
vector<int>v(n+1,0);
for(int i=0;i<m;i++)
{ cin>>x>>y;
if(x==0||y==0){break;}
uni(x,y);//并查集
//uni(y,x);
v[x]=1;v[y]=1;//将出现过的节点保存
}
//出现过的节点的父亲放入set,最后看set的size即可知道有几个连通图,还差几个
//比如4个节点,有2个连通图,且包含了3个节点
//那么把两个连通图连起来一条边,把第四个节点加入再一条边
int num=count(v.begin(),v.end(),1);
for(int i=1;i<=n;i++)
{
if(v[i]==1)//访问过,就判断属于哪个连通子图
{
s.insert(find(i));
}
}
int num_lt=s.size();
auto it=s.begin();
//cout<<fat[5]<<" "<<fat[6]<<fat[9]<<" "<<endl;
//cout<<n<<" "<<num<<" "<<num_lt<<endl;
cout<<n-num+num_lt-1<<endl;
}
return 0;
}
这次就AC了,大改一通
#include<bits/stdc++.h>
using namespace std;
/*
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。
省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通
(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。
问最少还需要建设多少条道路?
*/
vector<int>fat(1001,0);
int find(int x)
{ int ans;
if(fat[x]==0){return x;}
else //不是0那么肯定有了父亲,那就把路径压缩一下直达父亲,当然不压缩也行,就是会慢一点
{ ans=find(fat[x]);
fat[x]=ans;
}
return ans;
}
void uni(int x, int y)
{
if(find(x)!=find(y)){//本就在一起就不用连起来了,当然不剪枝也行,也是会慢一点
//int x=find(x);
fat[find(x)]=find(y);}//要把xy各自最顶层的连接起来,不能fat[x]=find(y),这样会连不起来
}
int main(){
//要用并查集
int n,m;
while(cin>>n>>m)
{ int x,y;
for(int i=1;i<=n;i++)
{
fat[i]=0;
}
vector<int>v(n+1,0);
for(int i=0;i<m;i++)
{ cin>>x>>y;
if(x==0||y==0){break;}
uni(x,y);//并查集
}
int num=0;
for(int i=1;i<=n;i++)
{
if(fat[i]==0)//没有成别人的子节点或者自己就是根或者孤立
{num++;
}
}
cout<<num-1<<endl;
}
return 0;
}