洛谷P3916
作者:互联网
题目描述
给出\(N\)个点,\(M\)条边的有向图,对于每个点\(v\),求\(A(v)\)表示从点\(v\)出发,能到达的编号最大的点。
输入输出格式
输入
第1行,2个整数\(N\),\(M\)。
接下来\(M\)行,每行2个整数U_i,V_i,表示边(U_i,V_i)。点用\(1,2,3\cdots N\)编号。
输出
\(N\)个整数\(\$A(1)\),\(A(2)\),\(\cdots,A(N)\)。
样例
输入样例
4 3
1 2
2 4
4 3
输出样例
4 4 3 4
思路
做这道题的方法不少。
在这里我只提一种
就是大法师(\(DFS\))。
可以采用反向建边,从最大的点开始\(DFS\)
我们考虑每次从所剩点中最大的一个点出发,我们暂且称它为\(i\),而凡是\(i\)这个点所能到达的点,可以到达的点最大都是\(i\)。
在遍历的时候按\(n\)——>\(1\)的顺序
因为是从大到小遍历,故每个点第一次被碰到的i一定是这个点最大可到达的点
上代码(走起)Music!!!
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int dfn[maxn],low[maxn],onStack[maxn],n,m,sum,res[maxn],cnt,scc[maxn],dp[maxn];
stack<int>s;
vector<int>e[maxn];
vector<int>new_e[maxn];
struct node{
int x,y;
}edge[maxn];
void tarjan(int u){
s.push(u);
onStack[u]=true;
dfn[u]=low[u]=++cnt;
for(size_t i=0;i<e[u].size();i++){
int next=e[u][i];
if(dfn[next]==0){
tarjan(next);
low[u]=min(low[u],low[next]);
}
else if(onStack[next]==true)
low[u]=min(low[u],low[next]);
}
if(dfn[u]==low[u]){
sum++;
onStack[u]=false;
scc[u]=sum;
res[sum]=max(res[sum],u);
while(s.top()!=u){
int cur=s.top();
s.pop();
onStack[cur]=false;
scc[cur]=sum;
res[sum]=max(res[sum],cur);
}
s.pop();
}
}
inline void dfs(int x){
if(dp[x]>0)
return ;
dp[x]=res[x];
for(size_t i=0;i<new_e[x].size();i++){
int next=new_e[x][i];
if(dp[next]==0)
dfs(next);
dp[x]=max(dp[x],dp[next]);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
edge[i].x=u;
edge[i].y=v;
e[u].push_back(v);
}
cnt=0;
for(int i=1;i<=n;i++)
if(dfn[i]==0)
tarjan(i);
for(int i=1;i<=m;i++){
int xx=scc[edge[i].x];
int yy=scc[edge[i].y];
if(xx!=yy)
new_e[xx].push_back(yy);
}
for(int i=1;i<=sum;i++)
if(dp[i]==0)
dfs(i);
for(int i=1;i<=n;i++)
printf("%d ",dp[scc[i]]);
return 0;
}
标签:洛谷,int,样例,到达,P3916,cdots,maxn,dp 来源: https://www.cnblogs.com/xzj213/p/10993897.html