题目
G题 Lexicographically Minimum Walk
题解
思路
要求s到t的字典序最小的路径,有可能不可达或路径无限长。
我们的贪心策略:要让字典序最小,那每次我们都要选取所相连的字典序最小的那条边,这样字典序能够做到最小,但是这样有可能走进死胡同里去了,而实际上s和t是可达的。
那么我们进行一个预处理,我们想要知道走哪些路是能到达t的,得出这些路之后,我们在这些路上进行上述的贪心策略即为答案。
怎么求s到t的可达路呢?
将原图反建,以t为起点跑一个dfs即可。这样被标记上的点都是可达t的,我们只在这些点上进行贪心策略,即可求出答案。
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <string.h>
#include <cstdio>
#include <cstdlib>
#include <queue>
using namespace std;
#define MP(a, b) make_pair(a, b)
typedef pair <int, int> P;
const int maxn = 1e5 + 10;
int n, m, s, t;
vector <P> G[maxn];
vector <int> rev_G[maxn];
bool rev_vis[maxn];
bool G_vis[maxn];
vector <int> ans;
void dfs_rev(int u){
rev_vis[u] = true;
for(int i = 0; i < rev_G[u].size(); i++)
if(!rev_vis[rev_G[u][i]])
dfs_rev(rev_G[u][i]);
}
void dfs(int u){
if(u == t){
for(int i = 0; i < ans.size(); i++){
printf("%d ", ans[i]);
}
exit(0);
}
if(G_vis[u]){
printf("TOO LONG\n");
exit(0);
}
G_vis[u] = true;
for(int i = 0; i < G[u].size(); i++){
if(rev_vis[G[u][i].first]){
ans.push_back(G[u][i].second);
dfs(G[u][i].first);
}
}
}
bool cmp(P a, P b){
return a.second < b.second;
}
int main(){
cin >> n >> m >> s >> t;
int u, v, c;
for(int i = 1; i <= m; i++){
scanf("%d %d %d", &u, &v, &c);
G[u].push_back(MP(v, c));
rev_G[v].push_back(u);
}
for(int i = 1; i <= n; i++){
sort(G[i].begin() , G[i].end(), cmp);
}
dfs_rev(t);//check rev_G
if(!rev_vis[s]){
return 0 * printf("IMPOSSIBLE\n");
}
dfs(s);//dfs G
return 0;
}