洛谷 P1027 Car 的旅行(计算几何)

洛谷 P1027 Car 的旅行(计算几何)

题意

中文题还写个p题意,复制粘贴在这里:

题目

思路

简单粗暴最短路,问题其实在计算几何的部分。每个城市给矩形三个点,然后计算第四个点,只需要先两两组合成向量,看是不是垂直(点积为0),如果是垂直,向量相加就是第四个点和刚才三个点的垂足形成的向量,加上垂足的坐标就是第四个点的坐标了。然后就是两两之间连上边,同一个城市是高铁线路,不同城市间是飞机,最后A城市跑四次最短路,一共求16次最小值即可。

麻烦还是很麻烦的,写了一节算法课加上晚上半小时调bug,可能是计算几何不太熟练。但是这个题真的一点坑都没有,一发CE(OI里默认不用C++11?不懂你们OI圈。。。emplace_back居然是C++11特性,学到了)之后就过了。

代码

#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
#include <cmath>
#include <cfloat>
#include <iomanip>

using namespace std;
struct vect
{
    double x;
    double y;
    vect()
    {
        x=0;
        y=0;
    }
    void fuck(double x1,double y1,double x2, double y2){
        x=x2-x1;
        y=y2-y1;
    }
    double operator * (vect v2){
        return x*v2.x+y*v2.y;
    }
    vect operator + (vect v2){
        v2.x+=x;
        v2.y+=y;
        return v2;
    }
    double distance(vect v2){
        return sqrt((x-v2.x)*(x-v2.x)+(y-v2.y)*(y-v2.y));
    }
}nodes[405];
struct edge
{
    int to;
    double dist;
    int nxt;
    edge(int t,double d,int nx):to(t),dist(d),nxt(nx){

    }
};
int egs[1005];
vector<edge> edges;
struct cmp
{
    bool operator() (const pair<double,int> a, const pair<double,int> b)const{
        if(a.first!=b.first)
            return a.first<b.first;
        else
            return a.second<b.second;
    }
};
void addedge(int f,int t,double w)
{
    //cout<<f<<" "<<t<<" "<<w<<endl;
    edges.emplace_back(t,w,egs[f]);
    egs[f]=edges.size()-1;
}
double dis[1000];
void dijsktra(int s)
{
    for(int i=0;i<=405;i++)
    {
        dis[i]=1000000007;
    }
    dis[s]=0;
    priority_queue<pair<double,int>,vector<pair<double,int> >, cmp > qq;
    //堆顶是最小元素,按pair的first排序,所以把dis值放first(当年T到怀疑人生。。)
    qq.push(make_pair(dis[s],s));
    //源点入堆
    while(!qq.empty())
    {
        double preval=qq.top().first;
        int x=qq.top().second;
        //cout<<x<<endl;
        //和未优化的版本一样找最小元素
        qq.pop();
        if(dis[x]<preval)
        {
            //如果目前的值已经比进堆时小了,就没必要再过一遍了,有的题会卡这个时间
            continue;
        }
        for(int j=egs[x];j!=-1;j=edges[j].nxt)
        {
            if(dis[edges[j].to]>dis[x]+edges[j].dist)
            {//一样的松弛操作,但是没有松驰过的点就没必要进堆了
                dis[edges[j].to]=dis[x]+edges[j].dist;
                qq.push(make_pair(dis[edges[j].to],edges[j].to));
            }
        }
    }
}
int main() {
    double s,t;
    int a,b;
    int n;
    cin>>n;
    while(n--)
    {
        edges.clear();
        memset(egs,-1,sizeof egs);
        cin>>s>>t>>a>>b;
        int points=0;
        for(int i=0;i<s;i++)
        {
            double x[4],y[4],tt;
            cin>>x[0]>>y[0]>>x[1]>>y[1]>>x[2]>>y[2]>>tt;
            vect v1,v2;
            double cornerx=0;
            double cornery=0;//垂足
            for(int j=0;j<3;j++)
            {
                v1.fuck(x[j],y[j],x[(j+1)%3],y[(j+1)%3]);
                v2.fuck(x[j],y[j],x[(j+2)%3],y[(j+2)%3]);
                if(v1*v2==0){//两向量垂直
                    cornerx=x[j];
                    cornery=y[j];
                    break;
                }
            }
            vect v3=v1+v2;
            x[3]=v3.x+cornerx;
            y[3]=v3.y+cornery;//从向量得到第四个点坐标
            for(int j=0;j<4;j++)
            {
                nodes[points].x=x[j];
                nodes[points].y=y[j];//把点存起来方便后来算飞机线
                points++;
            }
            int limi=points-4;//本城市的开始点
            for(int j=limi;j<points;j++)
            {
                for(int k=limi;k<points;k++)
                {
                    if(j==k)
                    {
                        continue;
                    }
                    addedge(j,k,tt*nodes[j].distance(nodes[k]));//高铁线
                }
                for(int k=0;k<limi;k++)//和之前的点连飞机线
                {
                    addedge(j,k,t*nodes[j].distance(nodes[k]));
                    addedge(k,j,t*nodes[j].distance(nodes[k]));
                }
            }
        }
        a--;
        b--;//因为我用0开始的点编号,所以都-1
        double ans=DBL_MAX;
        for(int i=a*4;i<(a+1)*4;i++)
        {
            dijsktra(i);//共跑四次最短路
            for(int j=b*4;j<(b+1)*4;j++)
            {
                ans=min(ans,dis[j]);//共求16次最小值
            }
        }
        cout<<fixed<<setprecision(1)<<ans<<endl;//保留一位小数输出
    }
    return 0;
}

参考了以下文章,在此表示感谢

维基百科向量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值