pat甲级A1030 Travel Plan (30分) dijkstra + dfs

本文探讨了如何使用堆优化的Dijkstra算法和DFS解决旅行计划问题,强调了在处理需要考虑边属性的最短路径问题时,邻接表存储图可能带来的不便。同时指出,在某些情况下,可以在Dijkstra过程中直接选取最优解,避免DFS的使用。
摘要由CSDN通过智能技术生成

A1030

堆优化dijkstra + dfs

教训

这种需要考虑边上一些属性来在最短路中选最优解的题目不宜使用邻接表来存储图,否则在记录每个点的前驱时,还需要存入相应的边,代码写的很难看。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 505, M = 5e5+10, inf = 1e9;
int h[N], e[M], ne[M], w[M], cost[M], idx, d[N];
bool st[N];
typedef pair<int, int> PII;
priority_queue<PII, vector<PII>, greater<PII> > heap;
//first存放节点,second存放边
vector<PII> pre[N], path, tmp;
int n, m, mincost = inf, start, ed;

void add(int a, int b, int d, int c){
	e[idx] = b;
	cost[idx] = c;
	w[idx] = d;
	ne[idx] = h[a];
	h[a] = idx;
	idx++;
}

void dijkstra(int x){
	fill(d, d+N, inf);
	d[x] = 0;
	heap.push({d[x], x});
	while(heap.size()){
		auto t = heap.top(); heap.pop();
		int v = t.second;
		
		if(st[v]) continue;
		//cout << v << endl;
		st[v] = true;
		
		for(int i = h[v]; i != -1; i = ne[i]){
			int j = e[i];
			if(d[j] > d[v] + w[i]){
				d[j] = d[v] + w[i];
				heap.push({d[j], j});
				pre[j].clear();
				pre[j].push_back({v, i});
				//printf("%d ---> %d  %d\n", v, j, d[j]);
			}
			else if(d[j] == d[v] + w[i]){
				pre[j].push_back({v, i});
				//printf("%d ---> %d   %d\n", v, j, d[j]);
			}
		}
	}
	
	return ;
}

void dfs(PII u){
	if(u.first == start){
		tmp.push_back(u);
		//printf("push: %d %d\n", u.first, w[u.second]);
		int c = 0;
		for(int i = tmp.size()-1; i >= 1; i --){
			c += cost[tmp[i].second];
			//printf("cost: %d\n", c);
		}
		
		if(mincost > c) {
			path = tmp;
			mincost = c;
		}
		
		tmp.pop_back();
		return ;
	}
	
	tmp.push_back(u);
	//printf("push: %d %d\n", u.first, w[u.second]);
	int j = u.first;
	for(int i = 0; i < pre[j].size(); i++){
		//printf("%d pre %d %d\n", j, pre[j][i].first, w[pre[j][i].second]);
		dfs(pre[j][i]);
	}
	tmp.pop_back();
}

int main(){
	memset(h, -1, sizeof h);
	cin >> n >> m >> start >> ed;
	for(int i = 0; i < m; i++){
		int a, b, d, ct;
		scanf("%d %d %d %d", &a, &b, &d, &ct);
		add(a, b, d, ct);
		add(b, a, d, ct);
	}
	
	dijkstra(start);
	// 2*m+1是一条不存在的边,选用其主要是为了设置起点
	dfs({ed, 2*m+1});
	
	
	for(int i = path.size()-1; i >= 0; i --){
		printf("%d ", path[i].first);
	}
	printf("%d %d", d[ed], mincost);
}

朴素dijkstra + dfs

#include <bits/stdc++.h>
using namespace std;

const int N = 505, inf = 0x3f3f3f3f;
bool st[N];
int g[N][N], cost[N][N], d[N];
int n, m, mincost = inf, start, ed;

vector<int> path, tmp, pre[N];



void dijkstra(int x){
	fill(d, d+N, inf);
	d[x] = 0;
	
	while(true){
		int v = -1;
		for(int i = 0; i < n; i++){
			if(!st[i] && (v == -1 || d[v] > d[i])){
				v = i;
			}
		}
		
		if(v == -1) break;
		st[v] = true;
		
		for(int i = 0; i < n; i ++){
			if(d[i] > d[v] + g[v][i]){
				d[i] = d[v] + g[v][i];
				pre[i].clear();
				pre[i].push_back(v);
			}
			else if(d[i] == d[v] + g[v][i]){
				pre[i].push_back(v);
			}
		}
	}
}

void dfs(int u){
	if(u == start){
		tmp.push_back(u);
		
		int c = 0;
		for(int i = tmp.size()-1; i > 0; i --){
			int a = tmp[i], b = tmp[i-1];
			c += cost[a][b]; 
		}
		
		if(mincost > c){
			mincost = c;
			path = tmp;
		}
		
		tmp.pop_back();
		return ;
	}
	
	tmp.push_back(u);
	for(int i = 0; i < pre[u].size(); i ++){
		dfs(pre[u][i]);
	}
	tmp.pop_back();
}

int main(){
	cin >> n >> m >> start >> ed;
	memset(g, 0x3f, sizeof g);
	
	for(int i = 0; i < m; i++){
		int a, b, dis, c;
		scanf("%d %d %d %d", &a, &b, &dis, &c);
		g[a][b] = g[b][a] = dis;
		cost[a][b] = cost[b][a] = c;
	}
	
	dijkstra(start);
	
	dfs(ed);
	
	for(int i = path.size()-1; i >= 0; i --){
		printf("%d ", path[i]);
	}
	
	printf("%d %d", d[ed], mincost);
}

dijkstra

这题也可以直接在dijkstra的过程中选出最优解,无需通过dfs选。当两条路的距离相等时,优先选择花费少的那一条。
#include <bits/stdc++.h>
using namespace std;

const int N = 505, inf = 0x3f3f3f3f;
bool st[N];
int g[N][N], cost[N][N], d[N], ct[N],pre[N];
int n, m, mincost = inf, start, ed;

void dijkstra(int x){
	fill(d, d+N, inf);
	d[x] = 0;
	
	while(true){
		int v = -1;
		for(int i = 0; i < n; i++){
			if(!st[i] && (v == -1 || d[v] > d[i])){
				v = i;
			}
		}
		
		if(v == -1) break;
		st[v] = true;
		
		for(int i = 0; i < n; i ++){
			if(d[i] > d[v] + g[v][i]){
				d[i] = d[v] + g[v][i];
				ct[i] = cost[v][i] + ct[v];
				pre[i] = v;
			}
			else if(d[i] == d[v] + g[v][i] && ct[i] > cost[v][i] + ct[v]){
				pre[i] = v;
				ct[i] = cost[v][i] + ct[v];
			}
		}
	}
}

void dfs(int u){
	if(u == start){
		printf("%d ", u);
		return ;
	}
	dfs(pre[u]);
	printf("%d ", u);
}

int main(){
	cin >> n >> m >> start >> ed;
	memset(g, 0x3f, sizeof g);
	
	for(int i = 0; i < m; i++){
		int a, b, dis, c;
		scanf("%d %d %d %d", &a, &b, &dis, &c);
		g[a][b] = g[b][a] = dis;
		cost[a][b] = cost[b][a] = c;
	}
	
	dijkstra(start);
	
	dfs(ed);
	
	printf("%d %d", d[ed], ct[ed]);
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值