1. 程式人生 > >[Luogu P2296][NOIP 2014]尋找道路

[Luogu P2296][NOIP 2014]尋找道路

div 參考 fine return ret namespace 一個 原因 front

emmm交了第8次才過。

這道題目測一道單源最短路問題,因此dijkstra或者spfa板子先準備好。因為題中對最短路有限定:

  1. 路徑上的所有點的出邊所指向的點都直接或間接與終點連通。
  2. 在滿足條件1的情況下使路徑最短。

而題中還說“題目保證終點沒有出邊。”,所以我們考慮反向處理,也就是說最短路徑上的點一定在以終點為根的搜索樹上,並且這些點的所有出邊一定也在這棵樹上。所以考慮dfs/bfs搜索圖,標記所有搜過的點,然後枚舉每個標記點的出邊所指向的點,如果不在樹上則刪除標記。這裏有一個坑點,如果直接對標記進行修改,由於樹上的編號和搜索順序沒有關系,會導致改標記的時候把沒掃到的點也改掉了,從而造成刪掉不該刪掉的點,因此考慮備份標記即可。

然後我錯這麽多次的原因,說起來非常水,原因是在spfa的時候沒有對入隊的元素標記,數據大的時候入隊多次直接爆空間,只有第一個點數據小能水10分QAQ

參考代碼:

#include<iostream>
#include<cstdio>
#include<queue>
#define N 10010
#define M 200010
#define inf 1e8
using namespace std;
queue<int>q;
int nxt[M],to[M],fnxt[M],fto[M];
int n,m,in_q[N],head[N],vis[N],fhead[N],fcnt,cnt,visited[N],dis[N],s,t;
void dfs(int x) { visited[x] = 1; for(int i = fhead[x];i;i = fnxt[i]) { if(!visited[fto[i]]) dfs(fto[i]); } } void spfa() { for(int i = 1;i <= n;i++) dis[i] = inf; in_q[s] = 1; dis[s] = 0; q.push(s); int u; while(!q.empty()) { u
= q.front(); q.pop(); in_q[u] = 0; if(!visited[u]) continue; for(int i = head[u];i;i = nxt[i]) { if(dis[to[i]] > dis[u] + 1) { dis[to[i]] = dis[u] + 1; if(!in_q[to[i]]) { in_q[to[i]] = 1; q.push(to[i]); } } } } } void del() { for(int i = 1;i <= n;i++) vis[i] = visited[i]; for(int i = 1;i <= n;i++) { if(!vis[i]) { for(int j = fhead[i];j;j = fnxt[j]) { if(vis[fto[j]]) visited[fto[j]] = 0; } } } } void add(int u,int v,int k) { if(k == 1) { to[++cnt] = v; nxt[cnt] = head[u]; head[u] = cnt; } else { fto[++fcnt] = v; fnxt[fcnt] = fhead[u]; fhead[u] = fcnt; } return; } int main() { scanf("%d %d",&n,&m); int u,v; for (int i = 1;i <= m;i++) { scanf("%d %d",&u,&v); add(u,v,1); add(v,u,0); } scanf("%d %d",&s,&t); dfs(t); del(); spfa(); printf("%d",(dis[t] >= inf) ? -1 : dis[t]); }

[Luogu P2296][NOIP 2014]尋找道路