https://www.luogu.org/problemnew/show/P1027
这个题我一看认为是个裸的最短路(后来发现的确是个裸的最短路emmm)。但是,对于每一个地方,你只知道三个机场的坐标,另外一个你得根据其他三个机场的坐标和“四个机场呈矩形”这几个条件去确定。
好了,下面来一发高中数学。
当前需要解决的问题:已知点确定一个矩形,给定点的坐标,求点的坐标。
我们可以暴力枚举这个矩形的长和宽可能由那些点确认。即暴力枚举由当前点可构成的两条矩形的边交于这三个点中的哪个点。假设交于点。如果(以下直线的斜率用表示),则说明这个方案是合法的。同理也可推广到交于点或点的情况。
确定完构成矩形的两条边后,我们应该如何确定点呢?
把矩形的两条对边看作为两条向量(不知道它们的方向),那么。
利用这个性质,我们随便拿出之前由两个点(假设这两个点为,当前确定的两个边交于)确定的一条边, 指定方向为后算出这个向量的横纵坐标,然后以点为参照,作向量。向量。这样我们就确定完了。
确定完所有飞机场的坐标后。我们在同一个城市间的飞机场连边权=铁路单位费用*距离的边,不同城市间的飞机场连边权=航线单位费用*距离的边,以起点城市的四个城市为源点跑四遍最短路,每次跑完后拿当前源点与终点的四个飞机场的距离更新答案即可。
最后注意输出答案的小细节啊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;
}