Problem Description
给你n个城市m条单向的道路,q个访问。
问你u城市到v城市字典序最小的路径的第k个城市。
不存在路径的情况:
u->v城市的过程中存在一个使字典序一直变小的环。
u根本无法到达v。
思路:
一开始想到肯定是dfs,但是不知道如何跑,看了一些大佬的博客才知道如何处理。
我们预处理一下,所得dfs所走的下一个点,一定是字典序最小的。。
这里用到了tarjan算法的思想。我们在dfs的过程中,回到了祖先节点,代表这个环上的点有路径到达的其他点,全都不能到达,因为这个环会使得字典序不断变小。
然后就是如何判断该点是否是环(平时tarjan算法,单独的点也算一个环,这里的环是指两个点以上的环)上点,该点是否是可以到达的点。
具体看代码操作
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3005;
const int inf = 0x3f3f3f3f;
struct node
{
int u, v, k, id;
bool operator < (const node &b) const{
if(u == b.u) return v < b.v;
else return u < b.u;
}
};
node a[400055];
vector<int> Map[maxn], pos[maxn];
int path[maxn], num, dfn[maxn], low[maxn], vis[maxn];
int ans[400055];
void dfs(int u, int step, int ok)
{
dfn[u] = num++; low[u] = inf;//low[]初始化为inf很好,dfn[u]<low[u]就可以判断该点是否是环(这个环至少有两个点组成)上点。
vis[u] = 1;//用来标记是否是祖先
path[step] = u;//记录路径
if(ok)//代表这个点可以到达
{
for(int i = 0; i < pos[u].size(); i++)
{
int to = pos[u][i];
if(a[to].k <= step) ans[a[to].id] = path[a[to].k];
}
}
for(int i = 0; i < Map[u].size(); i++)
{
int to = Map[u][i];
if(!dfn[to])
{
dfs(to, step+1, ok && dfn[u]<low[u]);//当前点可以到达&&当前点不是环上的点。这样下一个点就是可以到达的
low[u] = min(low[u], low[to]);
}
else if(vis[to]) low[u] = min(low[u], dfn[to]);//祖先点
}
vis[u] = 0;
}
void add(int u, int v)
{
Map[u].push_back(v);
}
int main()
{
int n, m, q, u, v, i;
scanf("%d %d %d", &n, &m, &q);
while(m--) scanf("%d %d", &u, &v), add(u, v);
for(i = 1; i <= n; i++) sort(Map[i].begin(), Map[i].end());//预处理,这样dfs的是按照字典序走
for(i = 0; i < q; i++)
{
scanf("%d %d %d", &a[i].u, &a[i].v, &a[i].k);
a[i].id = i;
}
sort(a, a + q);//对于所有具有相同起始点的询问,都可以一起处理
memset(ans, -1, sizeof(ans));
int cnt = 0;
for(i = 1; i <= n; i++)
{
if(a[cnt].u == i)
{
while(a[cnt].u == i && cnt < q)
{
pos[a[cnt].v].push_back(cnt);//记录到达v,询问下标
cnt++;
}
num = 1;
memset(dfn, 0, sizeof(dfn));
memset(vis, 0, sizeof(vis));
dfs(i, 1, 1);
}
for(int j = 1; j <= n; j++) pos[j].clear();
}
for(i = 0; i < q; i++)
printf("%d\n", ans[i]);
return 0;
}