最短路变形二------反向建图技巧
第二类变形是针对有向图变形,不过还是让你求最短路。对于无向图来回最短路算一次乘以2就好了,但是对于有向图,极有可能从1到n的距离 和 从n到1的距离不相等,题目就是求从1到n,再从n会到1的最短路。当然点少的时候你可以选择floyd算法,但是存在重复边的时候或者点数巨大的floyd不好处理。那么引入第二种变形正向建立一次图跑dijkstra,反向建图跑一次dijkstra。来回加起来就是转一圈的最短距离。这样就能把复杂度降下来,但是空间复杂度上去了,典型的空间换时间。
Silver Cow Party
dijkstra + 小根堆 + 邻接表建图
//反向建图 + 两次dijkstra,好题。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int maxn = 1005;
const int inf = 1e9;
struct edge{
int x,y,z;
int k,s;
edge(int a,int b,int c){
x = a;y = b;z = c;
}
edge(int a,int b){
k = a;s = b;
}
bool operator <(const edge a)const{
return a.s < s;
}
};
vector<edge>e[2][maxn];
priority_queue<edge>q;
int ans[maxn];
int dist[maxn];
bool visited[maxn];
void clear_set()
{
fill(dist,dist+maxn,inf);
memset(visited,false,sizeof(visited));
while(!q.empty()){
q.pop();
}
}
void dijkstra(int x,int m)
{
dist[x] = 0;
q.push(edge(x,0));
while(!q.empty()){
edge p = q.top();
q.pop();
if(visited[p.k]){
continue;
}
visited[p.k] = true;
for(int i = 0;i < e[m][p.k].size();i++){
edge t = e[m][p.k][i];
if(!visited[t.y] && dist[t.y] > dist[p.k] + t.z){
dist[t.y] = dist[p.k] + t.z;
q.push(edge(t.y,dist[t.y]));
}
}
}
}
int main()
{
int m,n,k;
while(~scanf("%d%d%d",&n,&m,&k)){
for(int i = 0;i < maxn;i++){
e[0][i].clear();
e[1][i].clear();
}
memset(ans,0,sizeof(ans));
for(int i = 0;i < m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
e[0][x].push_back(edge(x,y,z));
e[1][y].push_back(edge(y,x,z)); //反向建图
}
clear_set();
dijkstra(k,0); //正向图的最短路
for(int i = 1;i <= n;i++){
ans[i] = dist[i];
}
clear_set();
dijkstra(k,1); //反向图的最短路
int mins = -1;
for(int i = 1;i <= n;i++){
if(i != k){
mins = max(mins,ans[i]+dist[i]);
}
}
printf("%d\n",mins);
}
return 0;
}
Invitation Cards
这道题的点边都有100w,非常大。杭电这个题卡你空间复杂度,也就是明确告诉你vector建图不行,要上链式前向星建图。北大这个题会卡你时间,也是要前向星建图,vector容器访问比较慢。
//HDU1535 , POJ1511
//链式前向星建正反图,跑两次dijkstra或者spfa
//不能用vector建图,会MLE.
// HDU POJ
//dijkstra 826ms 2063ms
//spfa 1232 2141ms
#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
const ll inf = 1e18;
const int maxn = 1e6+1e2;
struct edge{
int y;
int w;
int next;
}e[2][maxn];
struct info{
int k,s;
info(int a,int b){
k = a;s = b;
}
bool operator <(const info a)const{
return a.s < s;
}
};
priority_queue<info>q;
//queue<int>q;
int head[2][maxn],cnt[2];
bool visited[maxn];
ll dist[maxn];
void addedge(int k,int u,int v,int w)
{
e[k][cnt[k]].w = w;
e[k][cnt[k]].y = v;
e[k][cnt[k]].next = head[k][u];
head[k][u] = cnt[k]++;
}
void new_set()
{
cnt[0] = cnt[1] = 0;
for(int i = 0;i < maxn;i++){
head[0][i] = head[1][i] = -1;
}
}
void clear_set()
{
while(!q.empty()){
q.pop();
}
}
void dijkstra(int k)
{
for(int i = 0;i < maxn;i++){
dist[i] = inf;
visited[i] = false;
}
dist[1] = 0;
q.push(info(1,0));
while(!q.empty()){
info p = q.top();
q.pop();
if(visited[p.k]){
continue;
}
visited[p.k] = true;
for(int i = head[k][p.k];i != -1;i = e[k][i].next){
edge t = e[k][i];
if(!visited[t.y] && dist[t.y] > dist[p.k] + t.w){
dist[t.y] = dist[p.k] + t.w;
q.push(info(t.y,dist[t.y]));
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
new_set();
int n,m;
scanf("%d%d",&n,&m);
int x,y,z;
for(int i = 0;i < m;i++){
scanf("%d%d%d",&x,&y,&z);
addedge(0,x,y,z);
addedge(1,y,x,z);
}
clear_set();
dijkstra(0);
ll ans = 0;
for(int i = 1;i <= n;i++){
ans += dist[i];
}
clear_set();
dijkstra(1);
for(int i = 1;i <= n;i++){
ans += dist[i];
}
printf("%lld\n",ans);
}
return 0;
}