目录
1.错误的BFS
题目不做描述了,听完陈姥姥讲完怎么记录层数后就直接开始码了,也正是这个记录层数直接废了我一个下午,下面附上我第一次写出来的BFS并最终通过了测试点。
# include <iostream>
# include<vector>
# include <queue>
# define MAX 1005
using namespace std;
vector<int> all[MAX];
bool f[MAX];
int n,m;
int main(){
int i,j,a,b;
cin>>n>>m;
for(i=0;i<m;i++){
cin>>a>>b;
all[a].push_back(b);
all[b].push_back(a);
}
for(i=1;i<=n;i++){
queue<int> q;
int cnt=0,last=i,level=0,tail=0;
q.push(i);
while(!q.empty()){
int tmp=q.front();q.pop();
if(!f[tmp]){
f[tmp]=1;cnt++;
if(level<6){
int pos=all[tmp].size();
for(j=0;j<pos;j++){
if(!f[all[tmp][j]]){
q.push(all[tmp][j]);
tail=all[tmp][j];
}
}
if(tmp==last){
level++;last=tail;
}
}
}
}
printf("%d: %.2lf%\n",i,cnt*1.0/n*100);
for(j=0;j<MAX;j++) f[j]=0;
}
return 0;
}
但是提交上去最后一个测试点一直是答案错误。搜的AC题解是在每次入队列前进行去重,注意到我这里没有进行标记,所以可能导致一个结点被多次入队,但是没有关系,我在出队列后进行了标记,按理来说应该没有问题。但是我忽略了一点:
如图,我们的入队顺序是从左到右,从上到下。用上面的代码7,8,9结点会被入队两次,在4结点连的7,8,9入队完了之后,结点9就会作为last。那么问题就来了, 在第一个9出队之后,就会错误地判断成第三层的last结点,但是实际上第三层还有5,6没有访问,就进行了下一层的循环。如果这种情况发生在第六层就会使最终结果小于实际值。
2.正确的BFS
在最终搞明白问题之后,我写出来正确的BFS。
for(i=1;i<=n;i++){
queue<int> q;
int cnt=0,last=i,level=0,tail=0;
q.push(i);
while(!q.empty()){
int tmp=q.front();q.pop();
f[tmp]=1;cnt++;
if(level<6){
int pos=all[tmp].size();
for(j=0;j<pos;j++){
if(!f[all[tmp][j]]){
f[all[tmp][j]]=1;
q.push(all[tmp][j]);
tail=all[tmp][j];
}
}
if(tmp==last){
level++;last=tail;
}
}
}
}
3.层数记录优化
以下受这篇文章启发:深入剖析-06-图3 六度空间 (30 分)_xie_shu的博客-CSDN博客
我上面说了第一个BFS的问题在于记录层数,那么回想陈姥姥给出的方法是怎样记录的,定义了三个变量,主要是level和last,在每次读到一个last之后就表示这一层已经遍历完了,然后level加1,并然last等于最后入队的结点。
那么我们可以用同样的方法,在每层结束之后向队列插入一个结束标识元素。该元素的值为非正数,其绝对值等于层数。
具体做法是在初始结点入队之后入队一个0,作为第0层的结束标识元素,然后读到了第0层的一个结点(第0层只有一个元素),然后将与其相连的结点入队,继续读取队列就发现了结束元素0,然后我们就向队列插入一个-1,如此循环,直到我们读到结束元素-6。
注意到,每一层有且只有一个结束元素,所以这种方法可以解决我的第一个BFS的问题。
以下附上代码:
for(i=1;i<=n;i++){
queue<int> q;
int cnt=0;
q.push(i);q.push(0);
while(!q.empty()){
int tmp=q.front();q.pop();
if(tmp==-6) break;
if(tmp<1) {q.push(tmp-1);continue;}
if(!f[tmp]){
f[tmp]=1;cnt++;
int pos=all[tmp].size();
for(j=0;j<pos;j++){
if(!f[all[tmp][j]])
q.push(all[tmp][j]);
}
}
}
printf("%d: %.2lf%\n",i,cnt*1.0/n*100);
for(j=0;j<MAX;j++) f[j]=0;
}
当然,不管怎样将一个结点多次入队还是不划算的,还是要保证入队结点的唯一性。下面是最终代码:
for(i=1;i<=n;i++){
queue<int> q;
int cnt=0;
q.push(i);q.push(0);
while(!q.empty()){
int tmp=q.front();q.pop();
if(tmp==-6) break;
if(tmp<1) {q.push(tmp-1);continue;}
f[tmp]=1;cnt++;
int pos=all[tmp].size();
for(j=0;j<pos;j++){
if(!f[all[tmp][j]]){
f[all[tmp][j]]=1;
q.push(all[tmp][j]);
}
}
}
printf("%d: %.2lf%\n",i,cnt*1.0/n*100);
for(j=0;j<MAX;j++) f[j]=0;
}