洛谷 P1027Car的旅行路线题解--zhengjun
题目描述
又到暑假了,住在城市 \(A\) 的 \(Car\) 想和朋友一起去城市旅游。
她知道每个城市都有 \(4\) 个飞机场,分别位于一个矩形的 \(4\) 个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第 \(i\) 个城市中高速铁路了的单位里程价格为 \(T_i\) ,任意两个不同城市的机场之间均有航线,所有航线单位里程的价格均为 \(t\)。
图例(从上而下)
机场
高速铁路
飞机航线
注意:图中并没有标出所有的铁路与航线。
那么 \(Car\) 应如何安排到城市B的路线才能尽可能的节省花费呢?她发现这并不是一个简单的问题,于是她来向你请教。
找出一条从城市 \(A\) 到 \(B\) 的旅游路线,出发和到达城市中的机场可以任意选取,要求总的花费最少。
输入格式
第一行为一个正整数 \(n\),表示有 \(n\) 组测试数据。
每组的第一行有 \(4\) 个正整数 \(s,t,A,B\)。
\(S\) 表示城市的个数,\(t\) 表示飞机单位里程的价格,\(A\),\(B\) 分别为城市\(A\),\(B\) 的序号。
接下来有 \(S\) 行,其中第 \(i\) 行均有 \(7\) 个正整数\(x_{i1},y_{i1},x_{i2},y_{i2},x_{i3},y_{i3},T_i\),这当中的 (\(x_{i1},y_{i1}\)),(\(x_{i2},y_{i2}\)),(\(x_{i3},y_{i3}\))分别是第 \(i\) 个城市中任意 \(3\) 个机场的坐标,\(T_i\)为第 \(i\) 个城市高速铁路单位里程的价格。
输出格式
共有 \(n\) 行,每行 \(1\) 个数据对应测试数据。
保留一位小数。
输入输出样例
输入 #1 复制
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
输出 #1 复制
47.5
说明/提示
【数据范围】
对于 \(100\%\) 的数据,\(1\le n \le 10\),\(1\le S \le 100\),\(1\le A,B \le S\)
思路
一看就是最短路,我们可以把每个城市的一个机场看成一个点,然后算出所有机场之间的距离,然后就可以计算,最后用 \(SPFA\) 或者 \(dijkstra\) (因为一定没有负权边,我用的是 \(SPFA\))
但是,前面的预处理有点难度,可能裸的 \(SPFA\) 就不会有蓝色标签了。
首要的问题就是如何在知道一个矩阵中的 \(3\) 个点从而推出第 \(4\) 个点。
就比如说这幅图假设我们已经知道了坐标为这些的点:
- \(x=2\ ,\ y=5\)
- \(x=5\ ,\ y=1\)
- \(x=7\ ,\ y=2\)
那么我们怎么求第四个点呢。也就是 \(x=4\ ,\ y=6\)
通过观察,我们发现:
一对对角线的 \(x\) 和 \(y\) 的和就是另一对对角线的 \(x\) 和 \(y\) 的和
所以,在给我们的 \(3\) 个点中,我们只要找到一对对角线,将它存在编号为 \(1\) , \(2\) 的点中,另一个点存在 \(3\) 号点中。
则 \(4\) 号点的坐标就是 \((x_1+x_2-x_3,y_1+y_2-y_3)\)
然后,问题就转换成了找一对对角线。
这还不简单,因为矩阵中的对角线一定是在这个矩阵内部最长的一条线之一(因为还有一条对角线)
所以,我们只要找到给我们的 \(3\) 个点中距离最大的 \(2\) 个点(运用勾股定律)
最后 \(SPFA\) 就不用我讲了吧(\(dfs\) 还是 \(bfs\) 随你,反正我喜欢 \(bfs\))
代码
#include<bits/stdc++.h>
#define maxn 401
#define maxm 160001
#define bh(x,y) ((x-1)*4+y)//将第x个城市的第y个机场直接用四进制的方法转换成十进制(其实就是强制编号)
using namespace std;
int n,s,t,a,b;
struct zj{
int x,y;
};
zj f[maxn][4];//存边,变量实在太多了,只好用f,一不小心重名了调死我了
int c[maxn];
int head[maxn],k,nex[maxm],to[maxm];
double v[maxm];
double d[maxm];
void add(int x,int y,double z){//链式前向星
to[k]=y;
v[k]=z;
nex[k]=head[x];
head[x]=k++;
}
int main(){
cin>>n;
while(n--){
k=0;
memset(head,-1,sizeof(head));
//链表清空
cin>>s>>t>>a>>b;
for(int i=1;i<=s;i++){
double x1,y1,x2,y2,x3,y3,maxx=0;
int x,y;
for(int j=0;j<3;j++)
cin>>f[i][j].x>>f[i][j].y;
cin>>c[i];
/*****************求第四条边****************/
for(int j=0;j<3-1;j++){
for(int l=j+1;l<3;l++){
x=f[i][j].x-f[i][l].x;
y=f[i][j].y-f[i][l].y;
if(sqrt(double(x*x)+double(y*y))>maxx){//找距离最长的两个点
x1=f[i][j].x;
y1=f[i][j].y;
x2=f[i][l].x;
y2=f[i][l].y;
x3=f[i][3-j-l].x;
y3=f[i][3-j-l].y;
maxx=sqrt(double(x*x)+double(y*y));
}
}
}
f[i][3].x=x1+x2-x3;
f[i][3].y=y1+y2-y3;//套公式
/**************************************************/
}
/*****************建边******************************************************/
for(int i=1;i<=s;i++){
for(int ii=1;ii<=s;ii++){
for(int j=0;j<4;j++){
for(int jj=0;jj<4;jj++){
if(i==ii&&j==jj)
continue;
double p=sqrt(double((f[i][j].x-f[ii][jj].x)*(f[i][j].x-f[ii][jj].x)+(f[i][j].y-f[ii][jj].y)*(f[i][j].y-f[ii][jj].y)));
if(i!=ii)
add(bh(i,j),bh(ii,jj),p*t);
else
add(bh(i,j),bh(ii,jj),p*c[i]);
}
}
}
}
/**************初始化*********************************************************/
queue<int> q;
for(int i=0;i<s*4;i++)
d[i]=0x3fffffff;
for(int i=0;i<4;i++){
q.push(bh(a,i));
d[bh(a,i)]=0;
}
/*************SPFA**********************************************************/
while(!q.empty()){
int x=q.front();
q.pop();
for(int pos=head[x];pos!=-1;pos=nex[pos]){
if(d[to[pos]]>d[x]+v[pos]){
d[to[pos]]=d[x]+v[pos];
q.push(to[pos]);
}
}
}
/****************找答案*******************************************************/
double ans=0x3fffffff;
for(int i=0;i<4;i++)
if(d[bh(b,i)]<ans)
ans=d[bh(b,i)];
printf("%0.1lf",ans);
}
return 0;
}