(L2-038)病毒溯源(拓扑)

该博客主要探讨了一道程序设计题目,涉及拓扑排序和最长路径问题。通过建立距离数组d和前驱节点数组pre,实现了找到从源点出发的最长变异链及其路径。在比较字典序时注意要从大到小比较节点编号。博主提供了完整的C++代码实现,并通过一个样例解释了算法的工作原理。
摘要由CSDN通过智能技术生成

题目链接:PTA | 程序设计类实验辅助教学平台

 输入样例

10
3 6 4 8
0
0
0
2 5 9
0
1 7
1 2
0
2 3 1

输出样例

4
0 4 9 1

 分析:这道题目显然是考察的拓扑排序,我们只要设置一个d数组记录每个点到源点的距离即可求出最长变异链长度,由于要输出路径,所以我们还需要设置一个pre数组记录每个点的前驱节点,这样我们就可以按照题意进行求最优变异链,这里提醒大家一点:由于我们利用pre数组求取路径时是从终点开始的,也就是说从前往后的路径节点对应的在数组中的下标是从大到小的,所以我们在比较字典序时也要先比较数组中编号大的数再比较编号小的数,这个地方容易忽视,其他没什么了,细节见代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<set>
#include<vector>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
const int N=1e4+10;
vector<int>p[N];
int ru[N],d[N],pre[N];//ru[i]记录第i个点的入度,d[i]记录第i个点到原点的变异链长度,pre[i]记录第i个节点的前驱节点 
int st[N],t[N];
void bfs(int x)//拓扑排序 
{
	queue<int>q;
	q.push(x);
	d[x]=1;
	while(!q.empty())
	{
		int begin=q.front();
		q.pop();
		for(int i=0;i<p[begin].size();i++)
		{
			d[p[begin][i]]=d[begin]+1;
			q.push(p[begin][i]);
			pre[p[begin][i]]=begin;
		}
	}
}
bool cmp(int *a,int len1,int *b,int len2)//比较字符数组a和b的字典序 
{
	for(int i=len1,j=len2;i>0&&j>0;i--,j--)//倒序比较,先比较数组中编号大的元素,再比较数组中编号小的元素
	{
		if(b[j]<a[i]) return true;
		else if(b[j]>a[i]) return false;
	}
	if(len1<len2)	return false;//当相同位置元素均相同时,数组元素多的字符串字典序大
	else return true;
}
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int k;
		scanf("%d",&k);
		for(int j=1;j<=k;j++)
		{
			int t;
			scanf("%d",&t);
			t++;//让编号从1开始 
			p[i].push_back(t);
			ru[t]++;
		}
	}
	for(int i=1;i<=n;i++)
		if(!ru[i]) bfs(i);
	int mx=0;
	for(int i=1;i<=n;i++)
		if(d[i]>mx) mx=d[i];//找寻最长变异链长度 
	bool flag=false;//记录现在是否已经找到一个最长变异链 
	int tt=0;
	for(int i=1;i<=n;i++)
	{
		if(d[i]==mx)
		{
			int t1=0,ti=i;
			while(ti)//求该变异链序列,由于是从终点开始遍历,所以倒序是对应的序列 
			{
				t[++t1]=ti;
				ti=pre[ti];
			}
			if(!flag)//如果在此之前还未找到一条最长变异链序列,那么就先将当前序列作为答案 
			{
				flag=true;
				for(int i=1;i<=t1;i++)
					st[i]=t[i];
				tt=t1;
			}
			else//比较两条变异链序列的字典序,不要忘记是倒序比较 
			{
				if(cmp(st,tt,t,t1))
				{
					for(int i=1;i<=t1;i++)
						st[i]=t[i];
					tt=t1;
				}
			}
		}
	}
	printf("%d\n",mx);
	for(int i=tt;i>=1;i--)//倒序输出
	{
		printf("%d",st[i]-1);
		if(i!=1) printf(" ");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值