题干
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;
}