HDU1535 && POJ1511

最短路变形二------反向建图技巧

第二类变形是针对有向图变形,不过还是让你求最短路。对于无向图来回最短路算一次乘以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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值