题解 P2296 【寻找道路】
主要思路:DFS + Dijkstra + 堆优化 + 反向思维
要不是写代码的时间问题我就用线段树优化了
首先,题目要求是路径上的所有点的出边所指向的点都直接或间接与终点连通。
我们可以先不考虑如何通过某点到达终点,我们想象如果通过一个点可以再到达终点,说明把边反过来后,终点可以到达这个点。
那我们可以建反边,通过DFS求出可以去向终点的点了。到达不了的不符合条件,我们可以直接删点了。
dfs:
int vis[mn];//表示是否去过
void dfs(int x){
if(vis[x]) return; vis[x] = 1;
for (int i = hh[x]; i; i=ee[i].nxt) if (!vis[ee[i].v])
dfs(ee[i].v);
//printf("%d\n", x);
}
我们如何删点?
因为我们最终还是要跑最短路,所以我们就可以把这条边边权开到不可能到达的长度。
像这样
go(x, 1, n, 1)
if (!vis[x])
for (int i = hh[x]; i; i = ee[i].nxt) {
int v = ee[i].v;
for (int j = h[v]; j; j = e[j].nxt)
e[j].w = 100000;
}
然后把反向的边变成正边(我是直接存的两种边,其实都一样),然后跑Dijkstra + 堆优化就好了。注意,如果我们把边权值变化后就不能拿bfs像原来一样那么做了
代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
#define go(i, j, n, k) for (int i = j; i <= n; i += k)
#define fo(i, j, n, k) for (int i = j; i >= n; i -= k)
#define rep(i, x) for (int i = h[x]; i; i = e[i].nxt)
#define mn 10010
#define mm 200010
#define inf 2147483647
#define ll long long
#define ld long double
#define fi first
#define se second
#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define bson l, r, rt
//#define LOCAL
#define mod
#define Debug(...) fprintf(stderr, __VA_ARGS__)
inline int read(){
int f = 1, x = 0;char ch = getchar();
while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
//This is AC head above...
struct edge{
int f, v, w, nxt;
edge(int _f = 0, int _v = 0, int _w = 1, int _nxt = 0) : f(_f), v(_v), w(_w), nxt(_nxt){}
} e[mm << 1], ee[mm << 1];
int p, h[mn],pp,hh[mn];
inline void add(int a, int b, int c = 1){
e[++p].nxt = h[a], h[a] = p, e[p].f = a, e[p].v = b, e[p].w = c;
}
inline void fadd(int a,int b){
ee[++pp].nxt = hh[a], hh[a] = pp, ee[pp].f = a, ee[pp].v = b;
}
//Dij + 堆优化
struct node{
int u,v;
bool operator <(const node &b) const{
return u > b.u;
}
};//重载运算符
int n,m,s,t;
int dis[mn];//起点到每个点的距离
priority_queue<node> q;//优先队列
inline void Dij(int s){//s为起点
go(i, 0, n, 1)
dis[i] = inf;
dis[s] = 0;
node o;
o.u = 0;o.v = s;
q.push(o);
while (q.size()){
int u = q.top().v, d = q.top().u;
q.pop();
if(d!=dis[u])
continue;
rep(i,u){
int v = e[i].v, w = e[i].w;
if (dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
node p;
p.u = dis[v], p.v = v;
q.push(p);
}
}
}
}
int vis[mn];
void dfs(int x){
if(vis[x]) return; vis[x] = 1;
for (int i = hh[x]; i; i=ee[i].nxt) if (!vis[ee[i].v])
dfs(ee[i].v);
//printf("%d\n", x);
}
inline void debug(){
go(i, 1, p, 1) printf("%d->%d %d\n", e[i].f, e[i].v, e[i].w);
go(i, 1, pp, 1) printf("%d->%d %d\n", ee[i].f, ee[i].v, ee[i].w);
}
int main(){
n = read(), m = read();
go(i,1,m,1){
int a = read(), b = read();
fadd(b, a);
add(a, b);
}
s = read(), t = read();
//debug();
dfs(t);
go(x, 1, n, 1)
if (!vis[x])
for (int i = hh[x]; i; i = ee[i].nxt) {
int v = ee[i].v;
for (int j = h[v]; j; j = e[j].nxt)
e[j].w = 100000;
}
//debug();
Dij(s);
if(dis[t]!=inf)
cout << dis[t];
else
cout << -1;
#ifdef LOCAL
Debug("\nMy Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
return 0;
}
第十六次发题解,希望帮助同学们建立反向思维
NOIP2018并不是结束,而是开始