【8.8gzoj综合】师生树【BFS】

在这里插入图片描述
在这里插入图片描述

分析

这道题细节很多:)

对于total,能够直接想到的就是枚举树根,有几个点没有父亲就有几棵树。

对于求第几代学生,其实就相当于BFS将图分层, f f f数组记录就行: f [ e [ i ] . t o ] . s = f [ q [ h ] ] . s + 1 f[e[i].to].s=f[q[h]].s+1 f[e[i].to].s=f[q[h]].s+1

但是这题的细节处理很关键:

  1. 一开始对于字符串的提取和存储。将36个字符转化为1~36作为结点的下标,并且记录哪个字符出现过,后面要跳过没出现过的字符不做处理。
  2. 输出的时候要用一个 t t t结构体替代 f f f结构体。结构体的两个域分别存自己的层数和原本的下标,因为要按层数排序,下标记录便于输出。然而 f f f 结构体不能被改变,所以要复制一个来输出。输出要按照格式搞,第一个单独输出,后面与前面不一样的输出一个“,”
  3. 另外,对于“NO answer!”的判断,其实就是一棵树上存不存在环,如果很多棵树只有环,才是没有答案,一开始的程序这里出现问题被hack了。

上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue> 
using namespace std;

struct node
{
	int to,next;
}e[100010];
struct donkey
{
    int s,p;
}f[100010];
struct donkeycpy
{
	int s,p;
}t[100010];

int n,cnt,ff,fff,bz;
int v[100010],fa[100010],ap[100010]; 
int hd[100010],tot;

void add(int x,int y)
{
	e[++tot]=(node){y,hd[x]};
	hd[x]=tot;
}

int cmp(donkeycpy l,donkeycpy r)
{
	if(l.s==r.s) return l.p<r.p;
	else return l.s<r.s;
}

bool reach(int x,int start,int target)
{
	for(int i=hd[x];i;i=e[i].next)
	{
		if(bz) return true;
		if(e[i].to==target||e[i].to==start)
		{
			bz=1;
			return true;
		} 
		reach(e[i].to,start,target);
	}
}

void bfs(int st)
{
	queue<int> q;
	v[st]=1;
	f[st].s=1;
	q.push(st);
	while(!q.empty())
	{
		int x=q.front();
		q.pop();
		v[x]=1;
		for(int i=hd[x];i;i=e[i].next)
		{
			bz=0;
			if(reach(e[i].to,e[i].to,x))//判断环 
			{
				ff=1;
				return;
			}
			f[e[i].to].s=min(f[e[i].to].s,f[x].s+1);
			if(!v[e[i].to]) 
			{
				v[e[i].to]=1;
				q.push(e[i].to);
			}
		}
	}
}

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		char c[10];
		scanf("%s",c+1);
		int x,y;
		if(c[2]>'9') x=c[2]-54;
		else x=c[2]-47;
		if(c[4]>'9') y=c[4]-54;
		else y=c[4]-47;
		add(x,y);
		f[x].p=x;
		f[y].p=y;
		fa[y]++;
		ap[x]=ap[y]=1;
	}
	for(int i=1;i<=36;i++)
	{
		if(fa[i]==0&&ap[i]) 
		{
			
			for(int j=1;j<=36;j++) f[j].s=0x3f3f3f3f;
			memset(v,0,sizeof(v));
			ff=0;
			bfs(i);
			if(ff==1) continue;//有环,跳过本树 
			cnt++;
		    cout<<cnt<<':';
		    for(int i=1;i<=36;i++) t[i].s=f[i].s,t[i].p=f[i].p;
		    sort(t+1,t+36+1,cmp);
		    if(t[1].p<=10&&ap[t[1].p]) cout<<t[1].p-1;
			else if(t[1].p>10&&ap[t[1].p]) cout<<char(t[1].p+54);
		    for(int j=2;j<=36;j++)
		    {
		    	if(!ap[t[j].p]||t[j].s==1061109567||t[j].s==0) continue;
		    	if(t[j].s!=t[j-1].s) cout<<',';
				if(t[j].p<=10) cout<<t[j].p-1;
				else cout<<char(t[j].p+54);
			}
			cout<<endl;
			
		}
	}
	if(cnt==0) cout<<"NO answer!";
	else cout<<"total="<<cnt;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值