题目链接: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;
}