PAT (Advanced Level) Practice 1076 Forwards on Weibo BFS遍历图

一、概述

求最大潜在转发次数。

由于有层数限制,因此使用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;
	}	
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值