Car的旅行路线 luogu P1027 (Floyd玄学Bug有点毒瘤)

时间:2020-04-09
本文章向大家介绍Car的旅行路线 luogu P1027 (Floyd玄学Bug有点毒瘤),主要包括Car的旅行路线 luogu P1027 (Floyd玄学Bug有点毒瘤)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

luogu题目传送门!

题目中让我们求出从城市A到城市B的最小花费。

不难看出,这是一道最短路的问题,将最小花费看作每条边的长度,用SPFA(Floyd)跑最短路即可。

然而,每个城市有4个机场,去每个机场的花费都不一样。因此,考虑将一个城市拆分成4个点,在存点的时候储存下城市编号(类似强联通编号),进行特判就好了。

如果是同一个城市的,边长以为铁路票价 * dis, 不同城市则为 plane * dis。

答案即为终点四个机场中最小的。

(勾股定理好评!)

#include <bits/stdc++.h>
using namespace std;
#define N 401
#define isdigit(c) ((c)>='0'&&(c)<='9')
#define min(a,b) ((a)>(b)?(b):(a))
/*比 STL 快?*/ 
 
inline int read(){
    int x = 0, s = 1;
    char c = getchar();
    while(!isdigit(c)){
        if(c == '-') s = -1;
        c = getchar();
    }
    while(isdigit(c)){
        x = (x << 1) + (x << 3) + (c ^ '0');
        c = getchar();
    }
    return x * s;
}

struct node{//存每个机场的信息 
    int x, y;
    int city, price;
} t[N << 2];
double d[N << 2];
int n, pla_pri, s, ht;

inline int square(int x){
    return x * x;
}

inline int dis(int a, int b){//暂时先不开根号,使用起来方便一些 
    return square(t[a].x - t[b].x) + square(t[a].y - t[b].y); //编号机场的距离 
}

inline void get4(int n1,int n2,int n3){
    int x4, y4;
    int x1 = t[n1].x, y1 = t[n1].y, x2 = t[n2].x, y2 = t[n2].y, x3 = t[n3].x, y3 = t[n3].y;
    int ab = dis(n1, n2);
    int ac = dis(n1, n3);
    int bc = dis(n2, n3);
    if(ab == ac + bc) x4 = x1 + x2 - x3, y4 = y1 + y2 - y3;//勾股定理,求出第四个点 
    else if(ac == ab + bc) x4 = x1 + x3 - x2, y4 = y1 + y3 - y2;
    else if(bc == ac + ab) x4 = x2 + x3 - x1, y4 = y2 + y3 - y1;
    t[n3+1].x = x4, t[n3+1].y = y4;
/*    printf("x1:%d y1: %d x2: %d y2: %d x3: %d y3: %d x4: %d y4: %d\n", x1,y1,x2,y2,x3,y3,x4,y4);
    分段检查程序可以防止写完之后 Debug 两小时 */
    return ;
}

void init(){
    int cac_city = 0;
    for(int i = 1;i <= (n << 2); i += 4){
        t[i].x = read(), t[i].y = read();
        t[i+1].x = read(), t[i+1].y = read();
        t[i+2].x = read(), t[i+2].y = read();
        t[i].price = t[i+1].price = t[i+2].price = t[i+3].price = read();//把价格记录下来 
        t[i].city = t[i+1].city = t[i+2].city = t[i+3].city = ++cac_city;//城市记录下来 
        get4(i, i+1, i+2);//寻找第四个点 
    }
    return ;
}

queue <int> q; 
bool vis[N << 2];

void spfa(){
    /*时刻不忘 n 是4倍!!不然玄学错误!!*/ 
    for(int i = 1;i <= (n << 2); i++)
        d[i] = 99999999.99;
    for(int i = 4 * (s - 1) + 1;i <= 4 * (s - 1) + 4; i++){//都能作为起点,所以4个点全部推进去 
        vis[i] = 1;
        q.push(i);
        d[i] = 0;
    }
    while(!q.empty()){//SPFA 
        int now = q.front();q.pop();
        vis[now] = 0;
        for(int i = 1;i <= (n << 2); i++){//反正全部有连边,直接 for 循环不香吗 
            if(i == now)continue;
            double cost;
            if(t[i].city == t[now].city){//如果在同一个城市 
                cost = t[i].price * (double)sqrt((double)dis(i, now));
            }
            else cost = pla_pri * (double)sqrt((double)dis(i, now));//连边的价值(距离) 
            if(d[i] > d[now] + cost){
                d[i] = d[now] + cost;
                if(!vis[i]){
                    vis[i] = 1;
                    q.push(i);
                }
            }
        }
    }
    return ;
}

int main(){
//    freopen("hh.txt", "r", stdin);
    int T = read();
    while(T--){
        n = read(), pla_pri = read(), s = read(), ht = read();
        if(s == ht){  //这里用SPFA可以不特判,但是如果用Floyd就要特判了 (初始化成了极大值) 
            puts("0.0");
            continue;
        }
        init();
        spfa();
        double ans = ~0u >> 1;
        for(int i = 4 * (ht - 1) + 1;i <= 4 * (ht - 1) + 1 + 3; i++)
            ans = min(d[i], ans);//答案为终点四个机场里面最小的 
        printf("%.1lf\n", ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/wondering-world/p/12667308.html