洛谷 P1027Car的旅行路线题解--zhengjun

题目描述

又到暑假了,住在城市 \(A\)\(Car\) 想和朋友一起去城市旅游。
她知道每个城市都有 \(4\) 个飞机场,分别位于一个矩形的 \(4\) 个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第 \(i\) 个城市中高速铁路了的单位里程价格为 \(T_i\) ,任意两个不同城市的机场之间均有航线,所有航线单位里程的价格均为 \(t\)

A_zjzj

图例(从上而下)

机场
高速铁路
飞机航线

注意:图中并没有标出所有的铁路与航线。

那么 \(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\) 个点。

A_zjzj

就比如说这幅图假设我们已经知道了坐标为这些的点:

  1. \(x=2\ ,\ y=5\)
  2. \(x=5\ ,\ y=1\)
  3. \(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;
}

谢谢--zhengjun

posted @ 2022-06-10 19:00  A_zjzj  阅读(35)  评论(0编辑  收藏  举报