P1027 Car的旅行路线

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/guapi2333/article/details/83544658

https://www.luogu.org/problemnew/show/P1027

这个题我一看认为是个裸的最短路(后来发现的确是个裸的最短路emmm)。但是,对于每一个地方,你只知道三个机场的坐标,另外一个你得根据其他三个机场的坐标和“四个机场呈矩形”这几个条件去确定。

好了,下面来一发高中数学。

当前需要解决的问题:已知点A,B,C,D确定一个矩形,给定点A,B,C的坐标,求点D的坐标。

我们可以暴力枚举这个矩形的长和宽可能由那些点确认。即暴力枚举由当前点A,B,C可构成的两条矩形的边交于这三个点中的哪个点。假设交于点A。如果(以下直线的斜率用k表示)k_{l_{(A,B)}}k_{l_{(A,C)}}=-1,则说明这个方案是合法的。同理也可推广到交于点B或点C的情况。

确定完构成矩形的两条边后,我们应该如何确定点D呢?

把矩形的两条对边看作为两条向量A,B(不知道它们的方向),那么< A,B> =0/\pi

利用这个性质,我们随便拿出之前由两个点(假设这两个点为A,B,当前确定的两个边交于B)确定的一条边, 指定方向为B->A后算出这个向量的横纵坐标,然后以点C为参照,作向量C->D。向量C->D=B->A。这样我们就确定完了。

确定完所有飞机场的坐标后。我们在同一个城市间的飞机场连边权=铁路单位费用*距离的边,不同城市间的飞机场连边权=航线单位费用*距离的边,以起点城市的四个城市为源点跑四遍最短路,每次跑完后拿当前源点与终点的四个飞机场的距离更新答案即可。

最后注意输出答案的小细节啊emmm(1.保留一位小数的处理公式;2.输出精确位数的小数时用printf函数输出(不能用cout直接输出"cout<<(某个数)"))。

Code:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#define ri register int
using namespace std;

const int MAXN=160020;
const double INF=1e18;
int tz,n,m,s,t,u[MAXN],v[MAXN],fst[MAXN],nxt[MAXN];
double wf,x[MAXN],y[MAXN],wt[MAXN],w[MAXN],fm[10],fz[10],nowx[10],nowy[10],dis[MAXN],ans;
bool book[MAXN];

double dist(int a,int b)
{
	return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}

void clean()
{
	m=0,ans=INF;
	memset(fst,0,sizeof(fst));
	memset(nxt,0,sizeof(nxt));
	memset(book,0,sizeof(book));
}

struct node{
	int num;
	double c;
};
priority_queue<node>Q;
bool operator<(const node &a,const node &b)
{
	return a.c > b.c;
}

void Dijkstra(int s)//跑最短路 
{
	memset(book,0,sizeof(book));
	for(ri i=1;i<=n;i++)	dis[i]=INF;
	dis[s]=0;
	Q.push((node){s,0});
	while(!Q.empty())
	{
		int nowx=Q.top().num; Q.pop();
		if(book[nowx])  continue;
		book[nowx]=1;
		for(ri k=fst[nowx];k>0;k=nxt[k])
		{
			if(dis[v[k]]>dis[u[k]]+w[k])
			{
				dis[v[k]]=dis[u[k]]+w[k];
				Q.push((node){v[k],dis[v[k]]});
			}
		}
	}
}

void mathmatic(int num)//利用解析几何根据三点确定一点 
{
	for(ri i=1;i<=3;i++)
	{
		int cnt=0;
		for(ri j=1;j<=3;j++)
		{
			if(j==i)  continue;
			cnt++;
			fm[cnt]=x[(num-1)*4+i]-x[(num-1)*4+j],nowx[cnt]=x[(num-1)*4+j];
			fz[cnt]=y[(num-1)*4+i]-y[(num-1)*4+j],nowy[cnt]=y[(num-1)*4+j];
		}
		if((fm[1]==0&&fz[2]==0)||(fm[2]==0&&fz[1]==0))
		{
			double lx=x[(num-1)*4+i]-nowx[1],ly=y[(num-1)*4+i]-nowy[1];
			x[num*4]=nowx[2]-lx,y[num*4]=nowy[2]-ly;
			break;			
		}
		if((fm[1]*fm[2])/(fz[1]*fz[2])==-1)
		{
			double lx=x[(num-1)*4+i]-nowx[1],ly=y[(num-1)*4+i]-nowy[1];
			x[num*4]=nowx[2]-lx,y[num*4]=nowy[2]-ly;
			break;
		}
	}
}

void build()//建图 
{
	for(ri i=1;i<=n*4;i++)
		for(ri j=1;j<=n*4;j++)
		{
			++m;
			u[m]=i,v[m]=j;
			if((i-1)-(i-1)%4==(j-1)-(j-1)%4)  w[m]=dist(i,j)*wt[i];
			else  w[m]=dist(i,j)*wf;
			nxt[m]=fst[u[m]],fst[u[m]]=m;
		}	
}

void work()
{
	clean();
	scanf("%d%lf%d%d",&n,&wf,&s,&t);
	for(ri i=1;i<=n;i++)	
	{
		for(ri j=1;j<=3;j++)  
			scanf("%lf%lf",&x[(i-1)*4+j],&y[(i-1)*4+j]);
		scanf("%lf",&wt[(i-1)*4+1]);
		for(ri j=2;j<=4;j++)	wt[(i-1)*4+j]=wt[(i-1)*4+1];
		mathmatic(i);
	}
	build();
	n*=4;
	for(ri i=1;i<=4;i++)
	{
		Dijkstra((s-1)*4+i);
		for(ri j=1;j<=4;j++)  ans=min(ans,dis[(t-1)*4+j]);
	}
	printf("%.1f",floor(ans*10+0.5)/10);
	//当明确规定输出多少位小数时最好用printf函数输出 
}

int main()
{
	scanf("%d",&tz);
	for(ri i=1;i<=tz;i++)	work();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/guapi2333/article/details/83544658
今日推荐