百度之星2009程序设计大赛 初赛第二场第四题解答
题目:
4. 我的地盘 (350分)
题目描述
百度公司的员工们在工作之余,经常以产品组为单位组织一些活动,包括吃大餐、春游秋游、公益活动、唱KTV、看电影、体育比赛等。这些活动有一个专业的名字,叫做team building,我们也亲切的称之为“bui”。
最近,地图产品组刚刚完成一个大项目,大家决定大bui一场。一阵七嘴八舌后,很多内容被提了出来,最终确定先打乒乓球,然后吃饭,最后K歌。问题是,谁也不知道有什么地方可以同时满足这三个需求。
不过没有什么问题可以难倒我们的工程师。很快,就有人写出了程序,为大家找到了合适的地点。
地图覆盖之处,皆为我的地盘。你想挑战一下我们的工程师吗?想为我们找出更合适的地点吗?那就来吧。
输入格式
第1行是一个整数k,表示某范围内所有的POI(Point of Interest)点数量,后续k行每行用5个字段描述一个POI点。它们的含义和格式如下表:
内容 数据格式 数据范围
POI编号 Int,唯一 [0,231-1]
POI类型 字符串 0-16字节(不超过15种类型)
POI级别 Int 0-5(越大表示越高级)
POI经度 Double [0,180],6位有效精度
POI纬度 Double [0,180],6位有效精度
需要注意的是这里的经纬度跟通常的经纬度范围是不一样的
随后是一个整数n(0 < n <= 20),表示共n组查询。以下n行,每行表示一组查询,格式为:
POI类型1 POI类型2 POI类型3 最低级别 最高级别
分别表示三个bui地点各自的类型、最低级别和最高级别。
输出格式
对于每组查询”t1 t2 t3 min max”,输出三个POI编号p1、p2、p3,满足:
•p1、p2、p3的类型分别为t1、t2和t3。
•p1、p2、p3的级别不小于min,不大于max。
•p1、p2、p3的两两欧几里得距离之和应尽量小。
输入数据保证至少存在一个解。
样例输入
5
1 休闲娱乐 3 11.122843 12.431021
2 餐饮服务 2 13.384021 10.230425
3 旅游景点 3 12.234492 9.234268
4 休闲娱乐 5 20.242391 39.304233
5 教育机构 1 42.243292 67.232065
1
休闲娱乐 餐饮服务 旅游景点 0 5
样例输出
1 2 3
点击此处下载一份POI数据。所有测试点中的POI数据都基于此数据生成。可能的变动包括:
•修改POI编号
•对经纬度加入随机干扰(变化不会超过1%)。
•修改POI类别和级别
•加入不超过1%的新点,各项属性均完全随机
共20组测试点,其中第i个测试点包含i组查询。1 <= i <= 20
注意事项
•对于每个测试点,设已知最优解为D,则不超过1.05D的任意解均是可以接受的。
•请不要把离线计算的结果保存在源代码中(例如,直接把某些输入的答案保存在常量数组中,读取输入后直接输出),否则本题得0分。
分析:由于不需要求最优解,因此不用全部遍历,使用贪心法寻找离 当前已选择点集合 最近的点,不过没有测试是否最优,比赛的时候提交的是没有优化的代码,比完后又花了半个钟才完成了以下代码,感觉编码速度还是有待加强。我的水平只能弄出这样的结果了,不过应该还有更优的解法。
#include <stdio.h>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct POI
{
int id;
string kind;
int level;
double x;
double y;
};
int main()
{
int k,n,i,j;
vector<POI> all;
scanf("%d",&k);
for(i=0;i<k;i++)
{
POI temp;
cin>>temp.id>>temp.kind>>temp.level>>temp.x>>temp.y;
all.push_back(temp);
}
scanf("%d",&n);
for(i=0;i<n;i++)
{
string t[3];
int min,max;
cin>>t[0]>>t[1]>>t[2]>>min>>max;
int index[3]={0},totalindex[3]={0};
index[0]=-1;
double totalmindis = 360*360*3;
//方法:第一个POI选择所有可能的值,然后对每个可能的第一个POI,选择离他最近的第二个POI,
//选好第二个后,选择离前面两个距离之和最小的那个为第三个POI,
//选好三个后计算总距离,如果小于前一个解则更新解,终结条件是找到所有可能的第一个POI
while(index[0]<(int)all.size())
{
double curtotal = 0;
for(j=0;j<3;j++)
{
double mindis1 = 360*360;
double mindis2 = 360*360*2;
for(k=0;k<all.size();k++)
{
if(t[j]==all[k].kind&&all[k].level>=min&&all[k].level<=max)
{
//第一个POI
if(j==0)
{
if(k>index[0])
{
index[j] = k;
break;
}
}
//第二个POI
else if(j==1)
{
double dis = (all[k].x-all[index[j-1]].x)*(all[k].x-all[index[j-1]].x)+
(all[k].y-all[index[j-1]].y)*(all[k].y-all[index[j-1]].y);
if(dis<mindis1)
{
index[j]=k;
mindis1 = dis;
}
}
//第三个POI
else if(j==2)
{
double dis=0;
int t;
for(t=0;t<j;t++)
{
dis += (all[k].x-all[index[t]].x)*(all[k].x-all[index[t]].x)+
(all[k].y-all[index[t]].y)*(all[k].y-all[index[t]].y);
}
if(dis<mindis2)
{
index[j]=k;
mindis2 = dis;
}
}
}
}
//计算当前的总距离
if(j==1)
curtotal += mindis1;
if(j==2)
curtotal += mindis2;
//判断是否已经试过第一个POI的所有可能
if(j==0&&k==all.size())
index[j]=k;
}
if(curtotal<totalmindis&&index[0]!=all.size())
{
totalindex[0] = index[0];
totalindex[1] = index[1];
totalindex[2] = index[2];
totalmindis = curtotal;
}
}
cout<<all[totalindex[0]].id<<" "<<all[totalindex[1]].id<<" "<<all[totalindex[2]].id<<endl;
}
return 0;
}
弄了一组测试数据:
15
1 休闲娱乐 5 20.242391 39.304233
2 餐饮服务 2 13.384021 10.230425
3 旅游景点 3 12.234492 9.234268
4 休闲娱乐 3 11.122843 12.431021
5 教育机构 1 42.243292 67.232065
6 餐饮服务 2 113.054282 29.098546357
7 旅游景点 5 116.834988 39.952290455
8 旅游景点 5 113.166512 22.922672903
9 餐饮服务 2 113.054439 29.0987291154
10 文化教育 3 113.282175 23.201096485
11 综合商场 5 113.166579 22.923254613
12 金融行业 1 117.219020 36.500982133
13 文化教育 5 113.755876 39.739886517
14 综合商场 3 114.120915 36.1436581215
15 金融行业 5 104.688191 30.1736361401
5
休闲娱乐 餐饮服务 旅游景点 0 5
餐饮服务 休闲娱乐 旅游景点 0 5
金融行业 文化教育 综合商场 0 5
旅游景点 餐饮服务 金融行业 0 5
文化教育 餐饮服务 金融行业 0 5
我的答案是:
4 2 3
2 4 3
12 13 14
7 9 12
13 9 12