[计算几何+最短路]P1027 [NOIP2001 提高组] Car 的旅行路线
首先,这道题明显需要计算几何的相关知识,我输入了一个城市的三个点,怎么知道另一个点呢?
看一张图。
是的,我只需要找到一个是 \(\frac{\pi}{2}\) 的角度,然后利用向量的平行就 ok 了。
怎么找 \(\frac{\pi}{2}\) 的角呢,只需要利用 \(k\) 是负倒数就 ok 了。
特别注意,如果是一条如 \(x=a,a\in R\) 的直线,我们无法用斜截式表示,所以我用了一个变量专门去存是否垂直于 \(x\) 轴。
同时,这个图建图需要将每一个飞机场连起来,然后用距离公式:
令 \(D(A,B)\) 为点 \(A\) 到点 \(B\) 的距离
\[D(A,B)=\sqrt{(A_x-B_x)^2+(A_y-B_y)^2}
\]
然后只需要反手一个堆优化的 dijk 就解决了,代码细节很多。
#include <bits/stdc++.h>
#define debug puts("I ak IOI several times");
#define pb push_back
using namespace std;
template <typename T>inline void read(T& t){
t=0; register char ch=getchar(); register int fflag=1;
while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
while(('0'<=ch&&ch<='9')){t=((t<<1)+(t<<3))+ch-'0'; ch=getchar();} t*=fflag;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){
read(t);read(args...);
}
template <typename T>inline void write(T x){
if(x<0) putchar('-'),x=~(x-1); int s[40],top=0;
while(x) s[++top]=x%10,x/=10; if(!top) s[++top]=0;
while(top) putchar(s[top--]+'0');
}
const int MAXN=10000,MAXM=1000000;
const double EPS=1e-7;
int T,head[MAXN],cnt,sta,en,n,m;
struct Point{
int x,y;
Point(){
x=0;y=0;
}
void R(){read(x),read(y);}
void print(){
cout<<x<<' '<<y<<endl;
}
};
struct Line{
Point A,B;
double k,b;
bool Ver=0,Lev=0;
Line(){
k=0,b=0,Ver=0,Lev=0;
}
void Calc(){
if(A.y==B.y){
Ver=1;
return;
}
if(A.x==B.x){
Lev=1;
return;
}
k=(A.y-B.y)*1.0/(A.x-B.x*1.0);
b=B.y*1.0-k*B.x;
}
};
bool ver(Line x,Line y){
if(x.Ver&&y.Lev) return 1;
if(x.Lev&&y.Ver) return 1;
if(x.Ver||x.Lev||y.Ver||y.Lev) return 0;
return (abs(x.k+1.0/(y.k))<=EPS);
}
struct City{
Point a,b,c,d;
double cost;
double dist(Point x,Point y){ return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y)); }
void calca(){
d.x=a.x+c.x-b.x;
d.y=a.y+c.y-b.y;
}
Point ccalc(int whi){
if(whi==1) return a;
if(whi==2) return b;
if(whi==3) return c;
if(whi==4) return d;
}
void R(){
a.R();b.R();c.R(); cin>>cost;
Line AB,BC,AC;
AB.A=AC.A=a; BC.A=AB.B=b; BC.B=AC.B=c;
AB.Calc(); BC.Calc(); AC.Calc();
//调整到 a 对 c, b 对 d
if(ver(AB,BC)){
// Do nothing
}else
if(ver(AB,AC)){
//cout<<AB.Ver<<' '<<AC.Lev<<' '<<AB.Lev<<' '<<AB.Ver<<endl;
swap(a,b);
}
else if(ver(BC,AC)) swap(b,c);
//调整结束,使用平行四边形牛刀公式
calca();
}
}cit[MAXN];
struct Edge{
int to;double val;int nxt;
}e[MAXM];
void add(int x,int y,double val){
//建立双向边
e[++cnt]={y,val,head[x]};
head[x]=cnt;
e[++cnt]={x,val,head[y]};
head[y]=cnt;
return;
}
void intt(){
memset(e,0,sizeof(e)); cnt=0;
read(n,m,sta,en);
for(int i=1;i<=n;++i){
cit[i].R();
/* cit[i].a.print();
cit[i].b.print();
cit[i].c.print();
cit[i].d.print();*/
}
// 这边建一手图
for(int i=1;i<=n;++i){
//首先是一个城市内建图
for(int ddx=1;ddx<=4;++ddx)
for(int ddy=ddx+1;ddy<=4;++ddy)
if(ddx!=ddy) add(i*4+ddx,i*4+ddy,cit[i].dist(cit[i].ccalc(ddx),cit[i].ccalc(ddy))*cit[i].cost);
for(int j=i+1;j<=n;++j)
//然后在各个飞机场建图
for(int dx=1;dx<=4;++dx)
for(int dy=1;dy<=4;++dy)
add(i*4+dx,j*4+dy,cit[i].dist(cit[i].ccalc(dx),cit[j].ccalc(dy))*m);
}
return;
}
struct Node{
double val;
int to;
bool operator < (const Node &node) const{
return val>node.val;
}
};
double minn(double a,double b,double c,double d){
if(a<b&&a<c&&a<d) return a;
if(b<a&&b<c&&b<d) return b;
if(c<a&&c<b&&c<d) return c;
if(d<a&&d<b&&d<c) return d;
}
priority_queue<Node>Q;
double dis[MAXN];
bool vis[MAXN];
void solve(){
intt();
while(!Q.empty())Q.pop();
memset(dis,98,sizeof(dis));
memset(vis,0,sizeof(vis));
for(int dx=1;dx<=4;++dx) dis[sta*4+dx]=0,Q.push({0,sta*4+dx});
while(!Q.empty()){
int u=Q.top().to; Q.pop();
//cout<<u<<endl;
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].val){
dis[v]=dis[u]+e[i].val;
Q.push({dis[v],v});
}
}
}
cout<<fixed<<setprecision(1)<<minn(dis[en*4+1],dis[en*4+2],dis[en*4+3],dis[en*4+4])<<endl;
return;
}
int main(){
read(T);
while(T--) solve();
return 0;
}
//Welcome back,Chtholly.