一、概述
求最大潜在转发次数。
由于有层数限制,因此使用BFS最好,BFS若是要实时维护层数,要使用结构体队列。
有一些语法方面的小坑。
二、分析
理解题意容易弄反。输入的是“编号为x的用户所关注的人”,而不是“编号为x的用户的粉丝”,这两种种情况下邻接矩阵的元素下标是不同的。
很显然这是一个有向图,我定义第n行为第n个用户,第n行的非零元素就是他的粉丝。按这种逻辑,需要如下形式输入:
for(int i=1;i<=N;i++)
{
int m;
scanf("%d",&m);
for(int j=0;j<m;j++)
{
int people;//输入的是关注的人,eg.1号关注2、3、4
scanf("%d",&people);
follow[people][i]=1;
followsum[people]++;
}
}
注意两个下标不要弄反了。
输入之后就是BFS了。如下:
int BFS(int i,int forwardsum)
{
//由于要保存层号信息,队列的元素要变为结构体
//使用new创建结构体时,结构体创建在堆中,函数用完后结构体不删除,因此建树时要用new
//不使用new时结构体创建在栈中,函数调用结束后删除
queue<node> q;
node temp;
temp.num=i;
temp.level=0;
q.push(temp);
vis[i]=1;
while(!q.empty())
{
node _temp;
_temp=q.front();
q.pop();
if(_temp.level>L)
return forwardsum;
forwardsum++;
int j=_temp.num;
int nowfollow=0;
for(int k=1;k<1010;k++)
{
if(nowfollow>=followsum[j])
break;
if(follow[j][k]!=0&&vis[k]==0)
{
node tempnode;
tempnode.num=k;
tempnode.level=_temp.level+1;
q.push(tempnode);
vis[k]=1;
nowfollow++;
}
else if(follow[j][k]!=0&&vis[k]!=0)
nowfollow++;
}
}
return forwardsum;
}
首先要注意的一点是:
对于有返回值的函数,必须在任何情况下都有有效的返回值。最开始我忘了在最后加return,然后结果一直是一个八位固定数字,应该就是未赋值时的内存中的值。
注意在BFS中,队列元素为结构体时,新建一个节点不用new,直接node node即可,
这里有一点容易晕:
BFS中使用的是结构体队列,而树的层序遍历中使用的是结构体指针队列,这个要搞清楚。
这是因为,BFS在使用前,没有节点,只有一张邻接矩阵;而层序遍历在使用前,已经有一个一大堆节点组成的树了。
那么问题来了,BFS使用节点是为了保存更多信息,因此需要结构体数组来储存;
而层序遍历需要的是根节点,从根节点出发去遍历一棵树,那么保存根节点需要根节点类型的队列。根节点的类型是什么呢?是node*,为什么用node*而不用node,因为node类型是在栈中新建的变量,不用new方法;而node*类型是在堆中建的变量,需要使用new方法。为什么必须在堆中建呢?因为既然层序遍历,那么需要这棵树一直存在,不能新建树的函数调用完了,树就没了,那算建个屁的树。这样一来,逻辑就理清了,如下:
需要新建一棵树→调用建树函数→为了保证函数调用结束后树存在→在堆中开辟空间→使用new方法→树的节点类型是node*→层序遍历需要保存节点→使用node*类型队列
需要保存节点层数信息→使用结构体node储存节点号和层数→没了就没了→直接在栈中创建→节点类型为node→使用node型队列
另外要注意,node*要配合->食用,node配合.食用。
BFS的套路就是:至少一个参数,是入口节点
建队列
入口节点入队列
vis置一
队列不空则循环
队头出队
逻辑处理
与队头有关的,未入过队的节点入队
vis置一
最后要注意一点,存在vis函数的算法在多次调用遍历函数时,每遍历一次要将vis全置零一次,而且只能for循环置零,当然你用什么mem函数应该也可以;但是不能用vis[1000]={0},就是初始化的方法,这个不行。
三、总结
BFS中需要记录层数的应用,与DFS需要记录路径的类似,都是经典的算法,要记住。
PS:代码如下:
#include<stdio.h>
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
//有向图,每行不为0的代表该行的用户的粉丝
int follow[1010][1010]={0};
int vis[1010]={0};
int followsum[1010]={0};
int forwardsum=0;
int N,L;
struct node
{
int num;
int level;
};
int BFS(int i,int forwardsum)
{
//由于要保存层号信息,队列的元素要变为结构体
//使用new创建结构体时,结构体创建在堆中,函数用完后结构体不删除,因此建树时要用new
//不使用new时结构体创建在栈中,函数调用结束后删除
queue<node> q;
node temp;
temp.num=i;
temp.level=0;
q.push(temp);
vis[i]=1;
while(!q.empty())
{
node _temp;
_temp=q.front();
q.pop();
if(_temp.level>L)
return forwardsum;
forwardsum++;
int j=_temp.num;
int nowfollow=0;
for(int k=1;k<1010;k++)
{
if(nowfollow>=followsum[j])
break;
if(follow[j][k]!=0&&vis[k]==0)
{
node tempnode;
tempnode.num=k;
tempnode.level=_temp.level+1;
q.push(tempnode);
vis[k]=1;
nowfollow++;
}
else if(follow[j][k]!=0&&vis[k]!=0)
nowfollow++;
}
}
return forwardsum;
}
int main()
{
scanf("%d %d",&N,&L);
for(int i=1;i<=N;i++)
{
int m;
scanf("%d",&m);
for(int j=0;j<m;j++)
{
int people;//输入的是关注的人,eg.1号关注2、3、4
scanf("%d",&people);
follow[people][i]=1;
followsum[people]++;
}
}
int K;
scanf("%d",&K);
for(int i=0;i<K;i++)
{
int query;
scanf("%d",&query);
int sum=BFS(query,-1);
printf("%d\n",sum);
for(int p=0;p<1011;p++)
vis[p]=0;
}
}