1. 程式人生 > >P1027 Car的旅行路線

P1027 Car的旅行路線

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;
}