第一次做大牛们口中的“阅读题”,顿时感觉智商被碾压...
虽然读懂题意后就是很简单的最短路,但网上大牛们不带注释的题解对于博主这种弱渣简直同天书一般orz
于是AC后将此(水)题整理一下,加些注释,以方便查阅,大牛们飘过就好orz
题目大意:给出起点、终点、车站的位置和一些公交线路,分三种情况输出:(1)起点离终点够近,走过去;(2)起点离终点太远,但起点和终点附近都有车站,求最小乘车数(一站到底就是1,每换乘1趟+1);(3)起点离终点太远,且附近无车站,或者不可达,就打出租车;
将乘车数看做距离就是最短路问题了,大概有三种建图方法:http://blog.youkuaiyun.com/qq564690377/article/details/8053877,这里博主采用的是最后一种
拿第二组输入为例,建好图后大概像这样:
很容易就看出从起点到终点要上下车共4次,也就是搭两趟车
这方法挺神奇的...不用考虑重复建点(两线路都过b点的话建两个点)
这题貌似有个神奇的结论:从起点走到车站,不搭公车,直接走到终点,应该会输出一个0(博主没试过)。当然就结果而言这是对的,不过有些微妙。
#include<bits/stdc++.h>
#define maxv 5010
using namespace std;
const int len[8]={5120,2560,1280,640,320,160,80,40};
struct Point{
int x,y;//点的位置(x,y)
inline void read(){cin>>x>>y;}//读入点的位置
inline void readFromS(){//单独处理start和end
string s;
int rx,ry;
cin>>s>>rx>>ry;
int dx=0,dy=0;
for(int i=0;i<8;i++){
if(s[i]=='1'||s[i]=='3') dy+=len[i];
if(s[i]=='2'||s[i]=='3') dx+=len[i];
}
x=dx+(rx<<2);y=dy+(ry<<2);
}
double Length(Point b){return sqrt((double)(x-b.x)*(x-b.x)+(double)(y-b.y)*(y-b.y));}//求距离
}ps[maxv],&star=ps[0],&fin=ps[1];//实点集,0为start,1为end
struct Edge{
int to,cost;
Edge(int to,int cost):to(to),cost(cost){}
};
vector<Edge> G[maxv+200];//邻接表
int inf,d[maxv];//d[]记录上下车次数(距离)
typedef pair<int,int> P;<dist,node>
void Dijkstra(){//最短路,没什么要讲的吧?
priority_queue<P,vector<P>,greater<P> > p;
memset(d,0x0f,sizeof d);
inf=d[1],d[0]=0;
p.push(P(0,0));
while(!p.empty()&&d[1]==inf){
int v=p.top().second,cost=p.top().first;p.pop();
if(d[v]<cost) continue;
int size=G[v].size();
for(int i=0;i<size;i++){
Edge e=G[v][i];
if(d[e.to]>d[v]+e.cost){
d[e.to]=d[v]+e.cost;
p.push(P(d[e.to],e.to));
}
}
}
}
bool hasline,tooshort;
const string walk("walk there"),taxi("take a taxi");
void SOLVE()
{
if(tooshort) {cout<<walk<<endl;return;}//太短
else if(!hasline) {cout<<taxi<<endl;return;}//start或end的其中之一离车站过远
Dijkstra();
if(d[1]==inf) cout<<taxi<<endl;
else cout<<(d[1]>>1)<<endl;//乘车次数=上下车次数和/2
}
void INPUT()
{
int N,M;
//start和end建点
star.readFromS();fin.readFromS();
tooshort=star.Length(fin)<=2000.0;//太短
G[0].clear(),G[1].clear();
//对每个车站分别建点
map<string,int> m;//将车站名转为数字
cin>>N;
string s;
int tot=2;
while(N--){
cin>>s;
m[s]=tot;
ps[tot++].read();
}
//给距离start或end一千米以内的车站连单向边(start->车站 或 车站->end)
bool sto=false,tof=false;
for(int i=2;i<tot;i++){
G[i].clear();
if(star.Length(ps[i])<=1000.0) G[0].push_back(Edge(i,0)),sto=true;//从start走到车站过程中不用上下车
if(fin.Length(ps[i])<=1000.0) G[i].push_back(Edge(1,0)),tof=true;
}
hasline=sto&&tof;//start和end是否都能到达车站
//给公交线路建点并将其连与对应车站连双向边(车站<->线路)
cin>>M;
int k;
while(M--){
cin>>k;
G[tot].clear();
while(k--){
cin>>s;
int no=m[s];
G[tot].push_back(Edge(no,1));//下车
G[no].push_back(Edge(tot,1));//上车
}
tot++;
}
}
void MAIN()
{
int T;
cin>>T;
while(T--) INPUT(),SOLVE();
}
int main()
{
ios::sync_with_stdio(false);
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
MAIN();
return 0;
}