【NOIP2001】Car的旅行线路
时间限制: 1 Sec 内存限制: 64 MB
提交: 122 解决: 55
【NOIP2001】Car的旅行线路
时间限制: 1 Sec 内存限制: 64 MB提交: 122 解决: 55
题目描述
又到暑假了,住在城市A的Car想和朋友一起去城市B旅游。 她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第I个城市中高速铁路了的单位里程价格为Ti,任意两个不同城市的机场之间均有航线,所有航线单位里程的价格均为t。 那么Car应如何安排到城市B的路线才能尽可能的节省花费呢?她发现这并不是一个简单的问题,于是她来向你请教。 任务: 找出一条从城市A到B的旅游路线,出发和到达城市中的机场可以任意选取,要求总的花费最少。
输入
第一行为一个正整数n(1≤n≤10),表示有n组测试数据。
每组的第一行有四个正整数s,t,A,B。 S(0<S≤100)表示城市的个数,t表示飞机单位里程的价格,A,B分别为城市A,B的序号,(1≤A,B≤S)。
接下来有S行,其中第I行均有7个正整数xi1,yi1,xi2,yi2,xi3,yi3,Ti,这当中的(xi1,yi1),(xi2,yi2),(xi3,yi3)分别是第I个城市中任意三个机场的坐标,TI为第I个城市高速铁路单位里程的价格。
输出
共有n行,每行一个数据对应测试数据,结果保留2位小数。
样例输入
(如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
1
3 10 1 3
1 1 1 3 3 1 30
2 5 7 4 5 2 1
8 6 8 8 11 6 3
样例输出
47.55
这道题目好坑好坑哒!!!!!!
让我们慢慢的一点一点的分析吧:
①:这明显是一道最短路径题目。
②:本题的难点莫过于已知矩形三个顶点坐标求第四个点了,下面让我们随便画个图分析分析。
假设我们已经知道ABC三点坐标,要求D点坐标,怎么办呢?
连接两条对角线,根据平行四边形对角线互相平分可得:O为AD中点,O也是BC中点。
根据中点坐标公式:P( (Xa+Xb)/2, (Ya+Yb)/2 )可得O点的坐标(如右上)。
那么我们就可以建立一个方程:
那么,移项可得:
同理,对于纵坐标:
那么,我们只需要确定哪个是A点就能算出D点坐标了(只确定B或C点是不行的,但是确定A点就可以了,因为剩下两点不需要考虑顺序,不需要考虑谁是B点,谁是C点)。
容易发现,∠A是个直角,那么AB⊥AC,所以AB、AC的斜率之积为-1。因此,我们只需计算出AB、AC的斜率,再相乘即可。
即可?
我们可以看到,A'B'确实垂直于A'C',但是它们乘积并不等于-1,所以需要特殊考虑。
③:计算出了所有飞机场的坐标后,就可以计算出所有路径要花的钱了。
同城的,就用距离乘以火车票钱;不是同城的,就用距离乘以飞机票钱。
(只用一次Dijk也可以,但是你需要新建一个虚拟节点,把四个起点连起来)
详见代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int Da=9876543;
struct Epic
{
int x,y;
}point[120][5];
int all,n,fly,walk,da,db;
double dis[420][420];
void fouth(int yx)
{
int a,b,c,d=4;
//如何判断abc点->有两条垂线的是a点
for(int i=1;i<=3;i++)
{
double k1,k2;//k1,k2:与另外两点的连线的斜率
int p=i,q=-2,r;
for(int j=1;j<=3;j++)//假设该点是A点,随机分配B点和C点
if(q==-2 and i!=j) q=j;
else if(q!=-2 and i!=j) r=j;
//算斜率
if(point[yx][p].x!=point[yx][q].x and point[yx][p].x!=point[yx][r].x)//不平行,不垂直
{
k1=(point[yx][p].y-point[yx][q].y)*1.0/(point[yx][p].x-point[yx][q].x);
k2=(point[yx][p].y-point[yx][r].y)*1.0/(point[yx][p].x-point[yx][r].x);
if(k1*k2>=-1.001 and k1*k2<=-0.999)
{
a=p,b=q,c=r;
break;
}
}//p、r连线垂直于x轴,计算另一边的斜率是否为零
else if(point[sx][p].x==point[yx][r].x)
{
if(point[yx][p].y-point[yx][q].y==0)
{
a=p,b=q,c=r;
break;
}
}//p、q连线垂直于x轴,计算另一边的斜率是否为0
else
{
if(point[yx][p].y-point[yx][r].y==0)
{
a=p,b=q,c=r;
break;
}
}
}//因为一定有一个直角,所以循环结束后,一定可以确定A,B,C的坐标
point[yx][d].x=point[yx][b].x+point[yx][c].x-point[yx][a].x;
point[yx][d].y=point[yx][b].y+point[yx][c].y-point[yx][a].y;
}
int main()
{
scanf("%d",&all);
while(all)
{
memset(point,0,sizeof point);
for(int i=1;i<=4*n;i++)
for(int j=1;i<=4*n;j++)
dis[i][j]=(i!=j)*Da;
scanf("%d %d %d %d",&n,&fly,&da,&db);
for(int i=1;i<=n;i++)
{
scanf("%d %d %d %d %d %d %d",&point[i][1].x,&point[i][1].y,&point[i][2].x,&point[i][2].y,&point[i][3].x,&point[i][3].y,&walk);
fouth(i);
for(int j=1;j<=4;j++)//dis[j][k]:j号飞机场到k号飞机场的最短路
for(int k=1;k<=4;k++)//注意一个城市有4个飞机场
dis[4*(i-1)+j][4*(i-1)+k]=dis[4*(i-1)+k][4*(i-1)+j]=(j!=k)*(walk)*(sqrt((point[i][j].x-point[i][k].x)*(point[i][j].x-point[i][k].x)+(point[i][j].y-point[i][k].y)*(point[i][j].y-point[i][k].y)));
}//以上为同城的坐火车的距离
for(int i=1;i<=4*n;i++)
for(int j=1;j<=4*n;j++)
if(dis[i][j]==0 and i!=j)//飞机的距离
dis[i][j]=fly*(sqrt((point[(i-1)/4+1][(i-1)%4+1].x-point[(j-1)/4+1][(j-1)%4+1].x)*(point[(i-1)/4+1][(i-1)%4+1].x-point[(j-1)/4+1][(j-1)%4+1].x)+(point[(i-1)/4+1][(i-1)%4+1].y-point[(j-1)/4+1][(j-1)%4+1].y)*(point[(i-1)/4+1][(i-1)%4+1].y-point[(j-1)/4+1][(j-1)%4+1].y)));
for(int k=1;k<=4*n;k++)//弗洛伊德
for(int i=1;i<=4*n;i++)
for(int j=1;j<=4*n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[j][k]);
double back=Da;
for(int i=1;i<=4;i++)//比较四个起点到终点的最短距离
for(int j=1;j<=4;j++)
back=min(back,dis[(da-1)*4+i][(db-1)*4+j]);
printf("%.2lf\n",back);
all--;
}
}