人道救援(C++实现逐行读取csv数据)---PTA实验C++

一、题目再现

“最近世界不太平啊!”胸怀世界、心系人类的我注视着桌面上的世界地图不禁感慨。现代战争,空军先行。我仿佛看到各国空军化作粗大的箭头在地图运动,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_asciilatlngiso2iso3population
San Salvador13.6989-89.1914SVSLV567698
Jakarta-6.2146106.8451IDIDN34540000
Port-au-Prince18.5425-72.3386HTHTI987310
Tokyo35.6897139.6922JPJPN37977000
Maputo-25.915332.5764MZMOZ1191613

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

}

五、测评详情

  • 17
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xixixiLucky

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值