题目描述
给出N个点,M条边的有向图,对于每个点v,求A(v)表示从点v出发,能到达的编号最大的点。
输入格式
第1 行,2 个整数N,M。
接下来M行,每行2个整数Ui,Vi,表示边(Ui,Vi)。点用1,2,⋯,N编号。
输出格式
N 个整数A(1),A(2),⋯,A(N)。
输入输出样例
输入 #1复制
4 3 1 2 2 4 4 3
输出 #1复制
4 4 3 4
说明/提示
• 对于60% 的数据,1≤N.M≤10^3;
• 对于100% 的数据,1≤N,M≤10^5。
如果从每个结点出发取寻找最大编号结点,需要进行O(n)次全图遍历,其复杂度将达到O(m*n),是很难承受的。换一个思路,把边反向,那么可以从最大下标结点开始遍历,总计O(m)复杂度的时间实现这一要求。
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int main(void)
{
int n,m;
cin>>n>>m;
vector<int> p[n+1]; //邻接表
int vis[n+1]={0}; //访问数组,兼具记录作用
for(int i=0;i<m;i++){
int x,y;
cin>>x>>y;
p[y].push_back(x); //反向存储是精髓
}
for(int i=n;i>=0;i--){ //从最大下标开始访问
if(!vis[i]){ //如果未被访问
vis[i]=i; //自己能到自己
queue<int> q;q.push(i); //访问队列,把当前节点入队
while(!(q.empty())){ //当队列不为空时
int x=q.front();q.pop(); //出队
for(int j=0;j<p[x].size();j++){ //扫其邻接节点
if(!vis[p[x][j]]){ //如果没被访问
vis[p[x][j]]=i; //更新其最远节点
q.push(p[x][j]); //同时入队
}
}
}
}
}
for(int i=1;i<=n;i++){
if(i>1)cout<<' ';
cout<<vis[i];
}
return 0;
}