一、题目再现
“最近世界不太平啊!”胸怀世界、心系人类的我注视着桌面上的世界地图不禁感慨。现代战争,空军先行。我仿佛看到各国空军化作粗大的箭头在地图运动,Moscow、Kyiv、Paris、London、New York……向这些地点~~发动空袭~~(口误)我是说,运送人道主义救援物资……要飞多远呢?显然不能在三维空间划直线,那样会钻到地球里面去的。
一番搜索后我找到了球面余弦公式,已知两点的经纬度可以算出对地心的夹角,再配合地球半径就能算出大圆航线上的最短距离。
- 已知两地纬度
alat, blat
、经度alng, blng
,那么两地与地心所成的球面角:= ArcCos(Sin(alat) * Sin(blat) + Cos(alat) * Cos(blat) * Cos(alng - blng))
单位是弧度。
- 地球当成个理想的圆球,半径按6378.1公里算。
- 于是最短距离对应的圆弧长等于弧度乘以半径。
我准备找些大城市的经纬度来计算下,看看和航空里程数据差多少。可惜网上找到的是份CSV
的数据,里面有些无用的列。还得写个程序从里面过滤出城市名称和经纬度来。
输入规格
- 数据前面部分是CSV格式的城市数据,首行是标题,说明各列的含义。
- 经度范围是正负180,纬度范围正负90,单位是角度。
- CSV数据到
=====
(5个等号)行为止。我想最多也就二百多个城市。 - 后续测试数据部分每行为一组:有两个城市的名称,用
,
隔开(城市名称可能有空格或连字符但没有逗号)。读取到EOF为止。数量较多,请逐行计算并输出。
输出规格
对每组测试数据,计算两城市间最短的飞行距离并输出一行结果(保留1位小数)。
样例输入
city_ascii,lat,lng,iso2,iso3,population
San Salvador,13.6989,-89.1914,SV,SLV,567698
Jakarta,-6.2146,106.8451,ID,IDN,34540000
Port-au-Prince,18.5425,-72.3386,HT,HTI,987310
Tokyo,35.6897,139.6922,JP,JPN,37977000
Maputo,-25.9153,32.5764,MZ,MOZ,1191613
=====
Tokyo,Jakarta
Jakarta,Tokyo
Maputo,Maputo
样例输出
5792.5
5792.5
0.0
样例解释
- 两城市间的距离应该是对称的。
- 可能有多余的城市数据,如"San Salvador"。
- 列的数量和顺序是固定的(废话,CSV文件都下载好了)。虽然有些列用不上,如iso2/population等。
- 另外,俄罗斯看起来好宽,东西跨度足有中国的三倍,真是这样吗?
- 补充了Maputo同城的情况,在GCC、VC结果不同,直接计算可能由于浮点误差导致NaN。
代码长度限制 16 KB 时间限制 400 ms
内存限制 64 MB 栈限制 8192 KB
二、前置知识
1、csv(comma-separated-value 逗号分隔值)文本格式:
就是将excel二维表的数据通过逐行加逗号分隔各列来存储excel表中的数据
第一行:各列属性值 column1、column2、···columnN
下面每一行:代表对应于第一列的各个属性的数据值
1.1 把csv文件在记事本中打开如下
1.2 把csv文件在excel表中打开如下
city_ascii | lat | lng | iso2 | iso3 | population |
San Salvador | 13.6989 | -89.1914 | SV | SLV | 567698 |
Jakarta | -6.2146 | 106.8451 | ID | IDN | 34540000 |
Port-au-Prince | 18.5425 | -72.3386 | HT | HTI | 987310 |
Tokyo | 35.6897 | 139.6922 | JP | JPN | 37977000 |
Maputo | -25.9153 | 32.5764 | MZ | MOZ | 1191613 |
2、角度与弧度之间的转换公式&&弧长公式
C++中对应pai(3.1415926···)的常量为M_PI,直接用即可
三、解题思路
1、定义结构体
struct City{//结构体名(结构体为自定义的数据类型)
string name;//城市名
double lat;//经度
double lng;//纬度
};
2、定义动态长度的数组---vector容器
vector<City> cities;//<City代表该数组存储的数据类型为City
cities.push_back(City{name,lat,lng});//push_back()函数将一个又一个City类型数据放入vector数组
3、getline()函数读取并处理csv文本信息---指定逗号分隔符
while(getline(cin,line)&&line!="====="){
if(line=="")continue;
stringstream ss(line);
string name,lat_str,lng_str,iso1,iso2,population;
//逗号是指定的分隔符,在读到逗号的时候,getline会停止读取数据,此时将读到的数据存到相对应的变量中
getline(ss,name,',');//读城市名
getline(ss,lat_str,',');//读经度名(字符串型)
getline(ss,lng_str,',');//读纬度名
getline(ss,iso1,',');//下面的数据与所解题无关,可获取也可不获取
getline(ss,iso2,',');
getline(ss,population,',');
//利用stod()函数将字符串类型的经纬度转成浮点型,便于后续计算最短距离
double lat=stod(lat_str);
double lng=stod(lng_str);
cities.push_back(City{name,lat,lng});
}
4、将需要人道救援的两个城市名与csv中的数据相匹配
//=====后面的数据为两个城市名 name1,name2,同样以分隔符分开,与上面数据处理方法一致
string name1,name2;
while(getline(cin,line)){
stringstream ss(line);//stringstream是C++标准库的一个类,将字符串line作为输入流或输出流进行处理 流记为ss
getline(ss,name1,',');//逐行读取,读取ss流,将读取到的字符串存储在string类型name1中
getline(ss,name2,',');
//利用索引将两个城市名与上面获得的每个城市的名字、经度、纬度进行一一匹配
int index1=-1;//设初始值为-1
int index2=-1;
for(unsigned int i=0;i<cities.size();i++){
//因为int有符合,而vector类型数组的cities.size(),为保证比较数据的一致性在int前加unsigned转成无符号数据类型
if(cities[i].name==name1){
index1=i;//cities[index1]存储了城市1的名字、经度、纬度
}
if(cities[i].name==name2){
index2=i;//cities[index2]存储了城市2的名字、经度、纬度
}
}
if(index1!=-1&&index2!=-1){//即两个城市名在已给数据中都能找到
City city1=cities[index1];
City city2=cities[index2];
distance(city1,city2);//调用求距离函数
}
}
5、求两个城市之间最短距离的distance()函数
void distance(City city1,City city2){
if(city1.name==city2.name){//!!!一定要判断两个城市是同一个的情况,否则double类型计算有可能会出现误差导致结果错误不为0.0
cout<<"0.0"<<endl;
}else{
double lat1=city1.lat*M_PI/180;//!题目中给的lat,lng值均为角度值要利用转换公式转为弧度值(角度值*pai/180)
double lat2=city2.lat*M_PI/180;
double lng1=city1.lng*M_PI/180;
double lng2=city2.lng*M_PI/180;
//题目给的计算公式
double a=acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lng1-lng2));
double r=6378.1;//不要忘记乘上半径以后才是真正的两地之间最短距离
double result=a*r;
cout<<fixed<<setprecision(1)<<result<<endl;//fixed<<setprecision(1)使结果值保留一位小数
}
}
四、完整C++代码实现
#include<iostream>
#include<cmath>
#include<sstream>
#include<iomanip>
#include<vector>
using namespace std;
struct City{
string name;
double lat;
double lng;
};
void distance(City,City);
int main(){
string line0;
cin>>line0;
string line;
vector<City> cities;
while(getline(cin,line)&&line!="====="){
if(line=="")continue;
stringstream ss(line);
string name,lat_str,lng_str,iso1,iso2,population;
getline(ss,name,',');
getline(ss,lat_str,',');
getline(ss,lng_str,',');
getline(ss,iso1,',');
getline(ss,iso2,',');
getline(ss,population,',');
double lat=stod(lat_str);
double lng=stod(lng_str);
cities.push_back(City{name,lat,lng});
}
string name1,name2;
while(getline(cin,line)){
stringstream ss(line);
getline(ss,name1,',');
getline(ss,name2,',');
int index1=-1;
int index2=-1;
for(unsigned int i=0;i<cities.size();i++){
if(cities[i].name==name1){
index1=i;
}
if(cities[i].name==name2){
index2=i;
}
}
if(index1!=-1&&index2!=-1){
City city1=cities[index1];
City city2=cities[index2];
distance(city1,city2);
}
}
}
void distance(City city1,City city2){
if(city1.name==city2.name){
cout<<"0.0"<<endl;
}else{
double lat1=city1.lat*M_PI/180;
double lat2=city2.lat*M_PI/180;
double lng1=city1.lng*M_PI/180;
double lng2=city2.lng*M_PI/180;
double a=acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos(lng1-lng2));
double r=6378.1;
double result=a*r;
cout<<fixed<<setprecision(1)<<result<<endl;
}
}