Choose the best route
这道题是杭电oj的一道题,这是一道多起点单终点的最短路问题,在这里会提供两种办法解决这种类型的问题,第一种是反向建边,第二种是超级源点。
1.反向建边
反向建边是一个很妙的操作(下面的超级源点也很妙),因为不可能调用多次Dijkstra来确定一堆起点到终点的最短路,这样肯定会tle(floyd就更不用说了),所以我们可以反过来想,将唯一的终点换成起点,将多个起点换成多个终点,这样一来只用调用一次Dijkstra就可以求出所有到唯一终点的点的最短路金,最后进行比较即可。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Os")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("Og")
#pragma GCC optimize("inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <ll , ll> pii;
typedef priority_queue <ll ,vector<ll> ,greater<ll> > xiao;
typedef priority_queue <ll ,vector<ll> ,less<ll> > da;
const int N=5e4 + 10,inf = 0x3f3f3f3f;
const ull P = 131;
int h[N],ne[N],e[N],idx,w[N];
int dist[N],en[N];
int n,m,s;
bool st[N];
void add(int a,int b,int c)
{
e[idx] = b;
ne[idx] = h[a];
w[idx] = c;
h[a] = idx++;
}
void Dijkstra()
{
dist[s] = 0; //起始点设置为终点
priority_queue <pii , vector<pii> , greater<pii> > heap;
heap.push({0 , s});
while(heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second,distance = t.first;
if(dist[ver] < distance)
{
continue;
}
for(int i = h[ver] ; i != -1 ; i = ne[i])
{
int j = e[i];
if(dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({dist[j] , j});
}
}
}
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0),cout.tie(0);
while(cin>>n>>m>>s)
{
memset(h , -1 , sizeof h);
memset(dist , inf , sizeof dist);
idx = 0;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(b , a , c); //反向建边
}
Dijkstra(); //求出来的是所有点到终点最短路径
int w;
cin>>w;
int ans = inf;
while(w--)
{
int x;
cin>>x;
ans = min(ans , dist[x]);
}
if(ans == inf)
{
ans = -1;
}
cout<<ans<<"\n";
}
}
以上便是反向建边的写法,关于反向建边还有一道例题
这道题因为是要返回途径最大编号点的编号,如果是正常的按顺序dfs整个图那么就要遍历n次图,肯定会tle,这种时候我们可以调转思路,运用逆向思维反向建图,然后从大编号开始遍历,每个编号遍历的时候把途径的点都给标记上,这样之后每次遍历的时候只用遍历之前没有被标记过的点就行了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <ll , ll> pii;
typedef priority_queue <ll ,vector<ll> ,greater<ll> > xiao;
typedef priority_queue <ll ,vector<ll> ,less<ll> > da;
const int N=2e6 + 10,M = 0x3f3f3f3f;
const ull P = 131;
int n,m;
int h[N],e[N],ne[N],idx,res[N];
bool st[N];
void add(int a,int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void dfs(int u,int k)
{
st[u] = 1;
res[u] = max(res[u] , k);
for(int i = h[u] ; i != -1 ; i = ne[i])
{
int j = e[i];
if(!st[j])
{
res[j] = max(res[j] , k);
dfs(j , k);
}
}
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0),cout.tie(0);
memset(h , -1 , sizeof(h));
cin>>n>>m;
for(int i = 1 ; i <= m ; i++)
{
int a,b;
cin>>a>>b;
add(b , a);
}
for(int i = n ; i >= 1 ; i--)
{
if(!res[i])
{
dfs(i , i);
}
}
for(int i = 1 ; i <= n ; i++)
{
cout<<res[i]<<" ";
}
}
2.超级源点
接下来为大家带来第二种解决这种多起点问题的方法,就是超级源点,所谓的超级源点就是在建完图以后再额外设置一个虚点0,这个点到所有可能的起点的权值都是0,之后使用Dijkstra的时候只需要把起点设置为这个新建立的虚点,然后求出这个虚点到每个点的最小路径,这样只需要跑一次Dijkstra即可,非常的巧妙。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Os")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("Og")
#pragma GCC optimize("inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <ll , ll> pii;
typedef priority_queue <ll ,vector<ll> ,greater<ll> > xiao;
typedef priority_queue <ll ,vector<ll> ,less<ll> > da;
const int N=2e5 + 10,inf = 0x3f3f3f3f;
const ull P = 131;
int h[N],e[N],ne[N],idx,w[N];
int dist[N];
bool st[N];
int n,m,s;
void add(int a,int b,int c)
{
e[idx] = b;
ne[idx] = h[a];
w[idx] = c;
h[a] = idx++;
}
int Dijkstra()
{
dist[0] = 0;
priority_queue <pii , vector<pii> , greater<pii> > heap;
heap.push({0 , 0});
while(heap.size())
{
auto t = heap.top();
heap.pop();
int ver = t.second,distance = t.first;
if(st[ver])
{
continue;
}
st[ver] = 1;
for(int i = h[ver] ; i != -1 ; i = ne[i])
{
int j = e[i];
if(dist[j] > distance + w[i])
{
dist[j] = distance + w[i];
heap.push({dist[j] , j});
}
}
}
if(dist[s] == inf)
{
return -1;
}else
{
return dist[s];
}
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0),cout.tie(0);
while(cin>>n>>m>>s)
{
memset(h , -1 , sizeof h);
memset(dist , inf , sizeof dist);
memset(st , 0 , sizeof st);
idx = 0;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a , b , c);
}
int w;
cin>>w;
while(w--) //设置超级源点
{
int a;
cin>>a;
add(0 , a , 0);
}
int t = Dijkstra();
if(t == -1)
{
cout<<-1<<"\n";
}else
{
cout<<t<<"\n";
}
}
}