天梯赛-最短路径-天梯地图&&直捣黄龙-题解

题干

7-28 天梯地图

本题要求你实现一个天梯赛专属在线地图,队员输入自己学校所在地和赛场地点后,该地图应该推荐两条路线:一条是最快到达路线;一条是最短距离的路线。题目保证对任意的查询请求,地图上都至少存在一条可达路线。
输入格式:
输入在第一行给出两个正整数N(2 ≤ N ≤ 500)和M,分别为地图中所有标记地点的个数和连接地点的道路条数。随后M行,每行按如下格式给出一条道路的信息:
V1 V2 one-way length time
其中V1和V2是道路的两个端点的编号(从0到N-1);如果该道路是从V1到V2的单行线,则one-way为1,否则为0;length是道路的长度;time是通过该路所需要的时间。最后给出一对起点和终点的编号。
输出格式:
首先按下列格式输出最快到达的时间T和用节点编号表示的路线:
Time = T: 起点 => 节点1 => … => 终点
然后在下一行按下列格式输出最短距离D和用节点编号表示的路线:
Distance = D: 起点 => 节点1 => … => 终点
如果最快到达路线不唯一,则输出几条最快路线中最短的那条,题目保证这条路线是唯一的。而如果最短距离的路线不唯一,则输出途径节点数最少的那条,题目保证这条路线是唯一的。
如果这两条路线是完全一样的,则按下列格式输出:
Time = T; Distance = D: 起点 => 节点1 => … => 终点


7-44 直捣黄龙
本题是一部战争大片 —— 你需要从己方大本营出发,一路攻城略地杀到敌方大本营。首先时间就是生命,所以你必须选择合适的路径,以最快的速度占领敌方大本营。当这样的路径不唯一时,要求选择可以沿途解放最多城镇的路径。若这样的路径也不唯一,则选择可以有效杀伤最多敌军的路径。
输入格式:
输入第一行给出2个正整数N(2 ≤ N ≤ 200,城镇总数)和K(城镇间道路条数),以及己方大本营和敌方大本营的代号。随后N-1行,每行给出除了己方大本营外的一个城镇的代号和驻守的敌军数量,其间以空格分隔。再后面有K行,每行按格式城镇1 城镇2 距离给出两个城镇之间道路的长度。这里设每个城镇(包括双方大本营)的代号是由3个大写英文字母组成的字符串。
输出格式:
按照题目要求找到最合适的进攻路径(题目保证速度最快、解放最多、杀伤最强的路径是唯一的),并在第一行按照格式己方大本营->城镇1->…->敌方大本营输出。第二行顺序输出最快进攻路径的条数、最短进攻距离、歼敌总数,其间以1个空格分隔,行首尾不得有多余空格。

两题题解

这两题都是最短路径问题,同时要还原路径,路径的条件选择。
放在一起说,原理一样
这里采用的Dijkstra队列优化来做,习惯了这个。其实数据量不大,用n^2的普通算法来做也没压力。
首先是最短路径(优先队列)算法框架。

void Dijkstrad(int id){
	priority_queue<P, vector<P>, greater<P> > Q;
	fill (dis, dis+MAX_N, INF);
	dis[id] = 0;
	Q.push(P(0, id));
	while (!Q.empty()){
		P t = Q.top();
		Q.pop();
		int v = t.second;
		if (dis[v] < t.first) continue;
		for (int i = 0; i < G[v].size(); i++){
			edge e = G[v][i];
			if (dis[e.to] > dis[v] + e.cost){
				dis[e.to] = dis[v] + e.cost;
				Q.push(P(dis[e.to], e.to));
			}
		}
	}
}

路径还原

是从记录中从后往前(终点->起点)回溯;因为题中最短路径选择的是唯一的。无论之前怎么改变,每个节点总是会对应一个他的前导节点,所以从终点往前回溯,就能还原出路径。

所以在Dijkstra的时候,用pre数组记录一下即可;初始化为-1,因为没有谁的id是-1;

void Dijkstrad(int id){
	fill (pre, pre+MAX_N, -1);
	...
	while (!Q.empty()){
		...
		for (...){
			...
			if (...){
				pre[e.to] = v;    //记录他的前导
			}
		}
	}
}

路径还原如下

vector<int> getPath(int v){
	vector<int> path;
	while (v!=-1){
		path.push_back(v);
		v = pre[v];    //回溯
	}
	return path;    //从终点到起点的路径
}

路径条件选择

这里貌似用了动态规划的思想(迷糊)
将每次使用了的节点的条件信息更新
再通过条件来进行选择,并且相关的路径记录也要更新
第一题中的最短距离的最少节点数量为条件举例;
接上面路径记录的代码框架(包括pre路径记录的代码)
nodes数组来记录

void Dijkstrad(int id){
	fill (nodes, nodes+MAX_N, 0);    //初始化全部节点为0个
	nodes[id] = 1;    //当前开始节点算一个
	...
	while (!Q.empty()){
		...
		for (...){
			...
			if (dis[e.to] > dis[v] + e.cost){
				pre[e.to] = v;    //记录他的前导
				nodes[e.to] = nodes[v]+1;    //更新节点信息
			}  //进入条件判断
			else if (dis[e.to] == dis[v] + e.cost){
				//如果当前选中节点e.to和v路径相同且e.to大于v的+1
				//也就是如果以v作中转到e.to优于e.to之前的路径
				if (nodes[e.to] > nodes[v]+1){
					pre[e.to] = v;    //更新他的前导
					nodes[e.to] = nodes[v]+1;    //更新节点信息
				}
			}
		}
	}
}

同理,如果是最快,又要最短的话;
条件和更新的情况为

if (distance[e.to] > distance[v] + e.cost){
	....
	distance[e.to] = distance[v] + e.cost;    //加上e的距离
}

如果多重条件,同理在if的后面按规律进行筛选

if  ...
	...
else if (aa == aaa + a){
	if (xx > xxx+x){
			//更新他的前导
			//更新节点信息
	}else if (xx == xxx+x){
		if (yy < yy + y){
			//更新他的前导
			//更新节点信息
		}else if (y..)
			.....
	}
}

题解代码



题一
对时间和距离分别Dijkstra,原理一模一样

#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long int
#define mst(x) memset(x, 0 ,sizeof x)
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(a,b) cout<<a<<" : "<<b<<endl;
#define INF 0x3f3f3f3f
const ll MAX_N = 1e3+5;
const ll mod = 1e9+7;

struct edge{
	int to, tcost, dcost;
	edge(int a, int b, int c){to = a, dcost = b, tcost = c;}
};
typedef pair<int, int> P;
vector<edge> G[MAX_N];

int ddis[MAX_N];
int dpre[MAX_N];
int dmark[MAX_N];
int tdis[MAX_N];
int tpre[MAX_N];
int tmark[MAX_N];
int N, M;


void Qdijkstrad(int id){
	priority_queue<P, vector<P>, greater<P> > Q;
	fill (ddis, ddis+MAX_N, INF);
	fill (dpre, dpre+MAX_N, -1);
	mst(dmark);
	ddis[id] = 0;
	Q.push(P(0, id));
	while (!Q.empty()){
		P t = Q.top();
		Q.pop();
		int v = t.second;
		if (ddis[v] < t.first) continue;
		for (int i = 0; i < G[v].size(); i++){
			edge e = G[v][i];
			if (ddis[e.to] > ddis[v] + e.dcost){
				ddis[e.to] = ddis[v] + e.dcost;
				dmark[e.to] = dmark[v] + 1;
				dpre[e.to] = v;
				Q.push(P(ddis[e.to], e.to));
			}else if (ddis[e.to] == ddis[v] + e.dcost){
				if (dmark[v]+1 < dmark[e.to]){
					dmark[e.to] = dmark[v] + 1;
					dpre[e.to] = v;
				}
			}
		}
	}
}

void Qdijkstrat(int id){
	priority_queue<P, vector<P>, greater<P> > Q;
	fill (tdis, tdis+MAX_N, INF);
	fill (tpre, tpre+MAX_N, -1);
	mst(tmark);
	tdis[id] = 0;
	Q.push(P(0, id));
	while (!Q.empty()){
		P t = Q.top();
		Q.pop();
		int v = t.second;
		if (tdis[v] < t.first) continue;
		for (int i = 0; i < G[v].size(); i++){
			edge e = G[v][i];
			if (tdis[e.to] > tdis[v] + e.tcost){
				tdis[e.to] = tdis[v] + e.tcost;
				tmark[e.to]= tmark[v] + e.dcost;
				tpre[e.to] = v;
				Q.push(P(tdis[e.to], e.to));
			}else if (tdis[e.to] == tdis[v] + e.dcost){
				if (tmark[v]+e.dcost < tmark[e.to]){
					tmark[e.to]= tmark[v] + e.dcost;
					tpre[e.to] = v;
				}
			}
		}
	}
}

vector<int> get_tpath(int v){
	vector<int> path;
	while (v!=-1){
		path.push_back(v);
		v = tpre[v];
	}
	return path;
}

vector<int> get_dpath(int v){
	vector<int> path;
	while (v!=-1){
		path.push_back(v);
		v = dpre[v];
	}
	return path;
}

int main()
{
//	FILE_IN;FILE_OUT;
	int u, v, one, time, leng;
	cin>>N>>M;
	for (int i = 0; i < M; i++){
		sc("%d%d%d%d%d", &u, &v, &one, &leng, &time);
		G[u].push_back(edge(v, leng, time));
		if (!one){
			G[v].push_back(edge(u, leng, time));
		}
	}
	sc("%d%d", &u, &v);
	Qdijkstrat(u);
	Qdijkstrad(u);
//	cout<<tdis[v]<<endl;	
//	cout<<ddis[v]<<endl;
	vector<int> tpath = get_tpath(v);
	vector<int> dpath = get_dpath(v);
	bool is_same = true;
	if (tpath.size() == dpath.size()){
		for (int i = 0; i < tpath.size(); i++){
			if (tpath[i] != dpath[i]){
				is_same = false;
				break;
			}
		}
	}else{
		is_same = false;
	}
	
	
	if (is_same){
		pr("Time = %d; Distance = %d:", tdis[v], ddis[v]);
		for (int i = tpath.size()-1; i >= 0 ; i--){
			if (i)pr(" %d =>", tpath[i]);
			else pr(" %d", tpath[i]);
		}
	}else{
		pr("Time = %d:", tdis[v]);
		for (int i = tpath.size()-1; i >= 0 ; i--){
			if (i)pr(" %d =>", tpath[i]);
			else pr(" %d", tpath[i]);
		}
		pr("\n");
		pr("Distance = %d:", ddis[v]);
		for (int i = dpath.size()-1; i >= 0 ; i--){
			if (i)pr(" %d =>", dpath[i]);
			else pr(" %d", dpath[i]);
		}
	}
	return 0;
}


题目二
题目中的多条件采用多重if else 嵌套来解决
字符串映射到ID,原理就和普通的最短路一样了
最后再将ID映射回字符串输出

#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long int
#define mst(x) memset(x, 0 ,sizeof x)
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(a,b) cout<<a<<" : "<<b<<endl;
#define INF 0x3f3f3f3f
const ll MAX_N = 1e3+5;
const ll mod = 1e9+7;

map<string, int>mark;   //建立映射
map<int, string>back;    //建立反映射
struct edge{
	int to, time;
	edge(int a, int b){to = a, time = b;}
};
typedef pair<int, int> P;    //time, id
vector<edge>G[MAX_N];
int cont[MAX_N];    //记录对应城市的敌人数量,用于杀敌数的跟新
int pre[MAX_N];    //路径的前导
int dis[MAX_N];    //距离
int nodes[MAX_N];    //节点数
int kills[MAX_N];   //杀敌数
int shortpath[MAX_N];
ll killo;    //路径的条数(DP跟新)
void Dijkstra(int id){
	fill(pre, pre+MAX_N, -1);
	fill(dis, dis+MAX_N, INF);
	mst(nodes);
	mst(kills);
	mst(shortpath);
	dis[id] = 0;
	priority_queue<P, vector<P>, greater<P> > Q;
	Q.push(P(0, id));
	shortpath[id] = 1;
	int u;
	while (!Q.empty()){
		P t = Q.top();Q.pop();
		u = t.second;
		if (dis[u] < t.first) continue;
		for (int i = 0; i < G[u].size(); i++){
			edge e = G[u][i];
			if (dis[e.to] > dis[u]+e.time){
				dis[e.to] = dis[u]+e.time;
				pre[e.to] = u;
				nodes[e.to] = nodes[u]+1;
				kills[e.to] =  kills[u]+cont[e.to];
				shortpath[e.to] = shortpath[u];
				Q.push(P(dis[e.to], e.to));
			}else if (dis[e.to] == dis[u]+e.time){
				shortpath[e.to] += shortpath[u];
				if (nodes[e.to] < nodes[u]+1){    //多节点 
					nodes[e.to] = nodes[u]+1;
					kills[e.to] =  kills[u]+cont[e.to];
					pre[e.to] = u;
				}else if (nodes[e.to] == nodes[u]+1){
					if (kills[e.to] < kills[u]+cont[e.to]){   //多杀敌 
						kills[e.to] =  kills[u]+cont[e.to];
						pre[e.to] = u;
					}
				}
			}
		}
	}
}

vector<int> getpath(int he){
	vector<int> path;
	while (he!=-1){
		path.push_back(he);
		killo += cont[he];
		he = pre[he];
	}
	return path;
}

int main()
{
//	FILE_IN;FILE_OUT;
	mst(cont);
	int N, K, cnt;cin>>N>>K;
	string u, v;
	string me, he;
	cin>>me>>he;
	mark[me] = 1;
	back[1] = me;
	for (int i = 2; i <= N; i++){
		cin>>u>>cnt;mark[u] = i;
//		debug("u", u);
		cont[i] = cnt;
		back[i] = u;
	}
	
	for (int i = 0; i < K; i++){
		cin>>u>>v>>cnt;
//		debug("cnt", cnt);
		G[mark[u]].push_back(edge(mark[v], cnt));
		G[mark[v]].push_back(edge(mark[u], cnt));
	}
	Dijkstra(mark[me]);
	killo = 0;
	vector<int> path = getpath(mark[he]);
	for (int i = path.size()-1; i >= 0; i--){
		cout<<back[path[i]];
		if (i) cout<<"->";
	}
	cout<<endl<<shortpath[mark[he]]<<" "<<dis[mark[he]]<<" "<<killo;
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hexrt

客官,请不要给我小费!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值