题目
众所周知,TT 有一只魔法猫。
今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!
输入
输入包含多组数据。每组数据第一行为 3 个整数 N, S 和 E (2 ≤ N ≤ 500, 1 ≤ S, E ≤ 100),即猫猫快线中的车站总数,起点和终点(即喵星机场所在站)编号。
下一行包含一个整数 M (1 ≤ M ≤ 1000),即经济线的路段条数。
接下来有 M 行,每行 3 个整数 X, Y, Z (1 ≤ X, Y ≤ N, 1 ≤ Z ≤ 100),表示 TT 可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。
下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
接下来 K 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。
输出
对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点),第二行是 TT 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行是 TT 前往喵星机场花费的总时间。
本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行
输入样例
4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3
输出样例
1 2 4
2
5
解题思路
1.首先,我们要计算走商业路的最短路径长度。
依次记录走每一条商业路的最短路径长度。
2.计算不走商业路的路径长度。
3.比较他们
4.注意:走商业路要注意左右点
设 起点为S,终点为E, 商业路的端点分别为 u ,v
则可以分为 S->u , v->E 和 S->v,u->E两种情况
则分别比较他们的大小,就得出走该条商业路的最短路径长度
5.注意输出格式
6.用pre作为前驱数组
代码实现
#include<iostream>
#include<vector>
#include<queue>
const int Max = 505;
const int INT_MAX_ = 200000;
using namespace std;
int N,S,E,M,X,Y,Z,K;
struct edge{
int u,v,w;
};
struct syx{ //记录对商业线两头的选择
int s_conect,e_conect,w_; //s连接的点,e连接的点
};
vector<edge> G[Max];
int dis[Max]; //装距离的
syx result[1005]; //用来存走了商业线的最小耗费
pair<int,int> remenber_s[1005]; //用来记录起始点到每条商业线左右两点的最小时间
pair<int,int> remenber_e[1005]; //记录终点 到每条商业线左右两点的最小时间
edge EE[1005]; //用来装商业线的
bool vis[Max]; //是否走过该点
priority_queue< pair<int,int> > q;
int pre1[Max] ; //记录每个点的前驱
int pre2[Max] ;
int EEi;
int rsi,rei;
int resulti;
void addE(int u,int v,int w) //添加边操作
{
edge E;
E.u=u,E.v=v,E.w=w;
G[u].push_back(E);
E.u=v,E.v=u,E.w=w;
G[v].push_back(E);
}
void ini(int x) //初始化 //x是起点
{
for(int i=0;i<=N;i++) dis[i]=INT_MAX_;
dis[x]=0;
while(!q.empty()) q.pop();
for(int i=0;i<Max;i++) vis[i]=0;
}
void dij(int s) //利用迪杰斯特拉算法求最短路径
{
ini(s);
pair<int,int> p; p.first = 0,p.second=s; //第一顺序比较的是距离
q.push(p);
while(!q.empty())
{
pair<int,int> x = q.top();
q.pop();
int poi = x.second;
if(vis[poi]) continue;
vis[poi] = true;
for(int i=0 ; i< G[poi].size();i++)
{
int y = G[poi][i].v, w = G[poi][i].w;
if( vis[y] ) continue;
if(dis[y] > dis[poi]+w)
{
dis[y] = dis[poi]+w; //更新
if(s==S) pre1[y] = poi; //记录S前驱
if(s==E) pre2[y] = poi; //记录E前驱
pair<int,int> PPy;
PPy.first = -dis[y] , PPy.second = y; //变成负距离来实现小根堆的效果
q.push(PPy);
}
}
}
}
int _res[1005];
int res_i;
int main()
{
ios::sync_with_stdio(0);
int COUNT1=0;
while(cin>>N&&cin>>S&&cin>>E)
{
if(!COUNT1) COUNT1=1; //控制输出回车的,最后一组和第一组不用输出回车
else cout<<endl;
for(int i=0;i<Max;i++) {pre1[i]=-1;pre2[i]=-1;}
EEi = rsi = rei = resulti = 0;
cin>>M;
for(int I=0;I<M;I++)//经济线
{
cin>>X>>Y>>Z;
addE(X,Y,Z);
cin>>K;
for(int I=0;I<K;I++)//商业线
{
cin>>X>>Y>>Z;
edge E_ ; E_.u = X,E_.v=Y,E_.w=Z;
EE[EEi++] = E_;
dij(S);
int Rus_1 = dis[E]; //不坐商业线的路径长度
for(int i=0;i<EEi;i++) //分别记录 s连接 u或v时的最短路径
{
remenber_s[rsi].first = dis[EE[i].u];
remenber_s[rsi].second = dis[EE[i].v];
rsi++;
}
dij(E);
for(int i=0;i<EEi;i++) //分别记录 e 连接u和v时的最短路径
{
remenber_e[rei].first = dis[EE[i].u];
remenber_e[rei].second = dis[EE[i].v];
rei++;
}
pair<int,int> remenber_res; //记录连了商业线哪个点
int min_res = INT_MAX_; //最小时间
for(int i=0;i<EEi;i++)
{//R1是S连U,E连V,R2是S连V,E连U
int r1 = remenber_s[i].first,r2 = remenber_e[i].second;
int r3 = remenber_s[i].second,r4 = remenber_e[i].first;
int R1 = r1+r2+ EE[i].w ,R2 = r3+r4+EE[i].w;
syx syx_;
if( R1 > R2 ) //R2是S连V,E连U
{
syx_.w_ = R2 ,syx_.s_conect= EE[i].v , syx_.e_conect = EE[i].u;
}
else //R1 是 S->u,E->v
{
syx_.w_ = R1 ,syx_.s_conect=EE[i].u, syx_.e_conect = EE[i].v;
}
//计算走了这条商业线的最短时间
if(min_res>syx_.w_) {
min_res = syx_.w_;
remenber_res.first = syx_.s_conect; //记录S链接的商业线点
remenber_res.second=syx_.e_conect; //记录E链接的商业线点
}
}
if( min_res > Rus_1 ) //不坐商业线快
{
int ii=E;
res_i=0;
int RES[505];int Ri=0;
while (ii!=-1)
{
RES[Ri++] = ii;
ii = pre1[ii];
}
for(int i=Ri-1;i>=0;i--)
{
_res[res_i++]=RES[i];
}
for(int i=0;i<res_i-1;i++)
cout<<_res[i]<<" ";
cout<<_res[res_i-1];
cout<<endl;
cout<<"Ticket Not Used"<<endl;
cout<<Rus_1<<endl;
}
else //坐商业线
{
int RES[505];int Ri=0;
res_i=0;
int i_ = remenber_res.first; //与S相连的商业线的一点
int ii = i_;
while(ii!=-1)
{
RES[Ri++]=ii;
ii = pre1[ii];
}
for(int ind=Ri-1;ind>=0;ind--)
_res[res_i++] = RES[ind];
int i__ = remenber_res.second;
while(i__!=-1)
{
_res[res_i++]=i__;
i__ = pre2[i__];
}
for(int i=0;i<res_i-1;i++) cout<<_res[i]<<" ";
cout<<_res[res_i-1];
cout<<endl;
cout<<i_<<endl;
cout<<min_res<<endl;
}
for(int i=0;i<=N;i++) G[i].clear();
}
return 0;
}
小结
这题很复杂,而且要考虑的细节很多。
最恶心人的就是格式输出了,不仅每组数据间要有一行空,而且最后一组数据还不能多回车。