CCF201712-4-行车路线

本文介绍了一种寻找最小疲劳度路径的算法实现,通过优化路径选择降低司机在不同路况下的疲劳积累,包括处理大道和小道的不同疲劳计算方式,采用Floyd算法预处理小道并结合Dijkstra算法进行最短路径搜索。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)题目大意:

问题描述
  小明和小芳出去乡村玩,小明负责开车,小芳来导航。
  小芳将可能的道路分为大道和小道。大道比较好走,每走1公里小明会增加1的疲劳度。小道不好走,如果连续走小道,小明的疲劳值会快速增加,连续走s公里小明会增加s2的疲劳度。
  例如:有5个路口,1号路口到2号路口为小道,2号路口到3号路口为小道,3号路口到4号路口为大道,4号路口到5号路口为小道,相邻路口之间的距离都是2公里。如果小明从1号路口到5号路口,则总疲劳值为(2+2)2+2+22=16+2+4=22。
  现在小芳拿到了地图,请帮助她规划一个开车的路线,使得按这个路线开车小明的疲劳度最小。
输入格式
  输入的第一行包含两个整数nm,分别表示路口的数量和道路的数量。路口由1至n编号,小明需要开车从1号路口到n号路口。
  接下来m行描述道路,每行包含四个整数tabc,表示一条类型为t,连接ab两个路口,长度为c公里的双向道路。其中t为0表示大道,t为1表示小道。保证1号路口和n号路口是连通的。
输出格式
  输出一个整数,表示最优路线下小明的疲劳度。
样例输入
6 7
1 1 2 3
1 2 3 2
0 1 3 30
0 3 4 20
0 4 5 30
1 3 5 6
1 5 6 1
样例输出
76
样例说明
  从1走小道到2,再走小道到3,疲劳度为52=25;然后从3走大道经过4到达5,疲劳度为20+30=50;最后从5走小道到6,疲劳度为1。总共为76。
数据规模和约定
  对于30%的评测用例,1 ≤ n ≤ 8,1 ≤ m ≤ 10;
  对于另外20%的评测用例,不存在小道;
  对于另外20%的评测用例,所有的小道不相交;
  对于所有评测用例,1 ≤ n ≤ 500,1 ≤ m ≤ 105,1 ≤ ab ≤ nt是0或1,c ≤ 105。保证答案不超过106


(二)解题思路:

这个题在官网上发出来以后,开始并没有想到什么解决的办法,但是看了数据范围之后发现骗70分还是比较简单的:其中的三个测试点n<=8,那么我们可以直接生成1~n的一个全排列,比如1->2->3->5->7->4->-6>8,表示一条1->8的路径,我们暴力找出所有可能路径的最小值便可以得出结果(路径上要记录连续走过的小路的长度),对于另外四个测试点,都是不存在连续的小路的,那么我们直接将小路的距离平方,接下来就是普通的求两点之间的最短路,这里就可以骗到70分了。

好了,接下来进入正题:这个题比较麻烦的是走小路的时候不是很方便处理,需要记录连续走多条小路时的总长度,后来便想到了先求出所有的连续走小路的情况,然后在求最终路径的时候不让走连续小路即可,具体操作为: 将所有的小路跑一遍floyd,跑完以后把所有的边平方。这样做完以后为什么可以转化为不让其走连续小路呢?仔细想想,如果两个点之间可以走连续小路到达,那么在跑完floyd后,将会有一条直接连接这两个点的路,且长度为这些小路长度和的平方。

做完上一个步骤之后还是求最短路,我们类似Dijkstra的做法,只不过要求图中不能走连续小路即可:对于到达每个的距离保存两个最小值,分别是走小路到达和走大路到达。每个点也保留两个状态,表示前驱路径分别小路和大路,然后某个点由小路到达的最短路径只能由前驱路径是大路的点去更新,说起来有点绕,具体细节参见代码:


(三)具体代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int maxn=520;
LL mps[maxn][maxn],n,m;
LL mpm[maxn][maxn];
LL dist[maxn][2];
void Deal_with_small(){
    for(register int k=1;k<=n;k++)
    for(register int i=1;i<=n;i++)
    for(register int j=1;j<=n;j++)
    if(mps[i][j]>mps[i][k]+mps[k][j])mps[i][j]=mps[i][k]+mps[k][j];
    for(register int i=1;i<=n;i++)
    for(register int j=1;j<=n;j++){
        if(mps[i][j]!=INF)mps[i][j]*=mps[i][j];
    }
}
void init(LL nn){
    for(register int i=1;i<=nn;i++)
    for(register int j=1;j<=nn;j++)
    mpm[i][j]=mps[i][j]=INF;
}
struct node{
    LL u,d;
    bool big;
    bool operator < (const node &n1)const{
        return u>n1.u;
    }
}beg,in,out;
LL Dijkstra(){
    priority_queue<node>Q;
    beg.big=1;beg.u=1;beg.d=0;
    for(register int i=1;i<=n;i++)dist[i][0]=dist[i][1]=(i==1?0:INF);
    Q.push(beg);
    while(!Q.empty()){
        out=Q.top();Q.pop();
        LL u=out.u,d=out.d;
        for(register int i=1;i<=n;i++){
            if(u==i)continue;
            if(out.big&&mps[u][i]!=INF){
                in.big=0;in.u=i;in.d=mps[u][i]+d;
                if(in.d<=dist[i][1]){dist[i][1]=in.d;Q.push(in);}
            }
            if(mpm[u][i]!=INF){
                in.big=1;in.u=i;in.d=mpm[u][i]+d;
                if(in.d<=dist[i][0]){dist[i][0]=in.d;Q.push(in);}
            }
        }
    }
    return min(dist[n][0],dist[n][1]);
}
int main(){
    LL x,y,d,t;
    scanf("%lld%lld",&n,&m);
    init(n);
    int ans=0;
    for(register int i=0;i<m;i++){
        scanf("%lld%lld%lld%lld",&t,&x,&y,&d);
        if(t)mps[x][y]=mps[y][x]=min(d,mps[y][x]);
        else mpm[x][y]=mpm[y][x]=min(d,mpm[y][x]);
    }
    Deal_with_small();
    cout<<Dijkstra()<<endl;
    return 0;
}

显然这个要跑floyd是O(n^3)的,n=500的话,一般情况下1s是比较难跑过的(这里优化一些细节后只跑了500+ms,CCF的测评姬强啊),所以这个做法并不是很优秀(真滴菜啊QWQ),日后再思考更加优秀的做法吧。


下面顺便给出那个骗分的70分代码大笑

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=520;
int mp[maxn][maxn],n,m;
int dist[maxn],done[maxn];
struct Edge{
    bool big;
    int Dist;
};
vector<Edge>edges;
int Small_m(int nn){
    int arr[12];
    int min_ans=INF;
    for(int i=0;i<nn;i++)arr[i]=i+1;
    do{
        if(arr[0]!=1)continue;
        int mid_ans=0,lastm=0;
        for(int i=0;i<nn;i++){
            if(!i)continue;
            if(mp[arr[i-1]][arr[i]]==INF)break;
            int edg=mp[arr[i-1]][arr[i]];
            if(edges[edg].big==0){
                mid_ans+=lastm*lastm+edges[edg].Dist;
                lastm=0;
                if(arr[i]==n){
                    min_ans=min(min_ans,mid_ans);
                    break;
                }
            }
            else{
                lastm+=edges[edg].Dist;
                if(arr[i]==n){
                    mid_ans+=lastm*lastm;
                    min_ans=min(min_ans,mid_ans);
                    break;
                }
            }
        }
    }while(next_permutation(arr,arr+nn));
    return min_ans;



}
void Dijkstra(){
    memset(done,0,sizeof(done));
    for(int i=1;i<=n;i++)dist[i]=(i==1?0:INF);
    for(int i=1;i<=n;i++){
        int x,m=INF;
        for(int y=1;y<=n;y++)if(!done[y]&&dist[y]<=m)m=dist[x=y];
        done[x]=1;
        for(int y=1;y<=n;y++)dist[y]=min(dist[y],dist[x]+mp[x][y]);
    }
}
int main(){
    Edge E;
    int x,y,d,t;
    memset(mp,0x3f,sizeof mp);
    cin>>n>>m;
    int ans=0;
    if(n<=10){
        for(int i=0;i<m;i++){
            cin>>t>>x>>y>>d;
            E.big=t;E.Dist=d;
            edges.push_back(E);
            mp[x][y]=mp[y][x]=i;
            ans=Small_m(n);
        }
    }
    else{
        for(int i=0;i<m;i++){
            cin>>t>>x>>y>>d;
            if(t)d*=d;
            mp[x][y]=mp[y][x]=d;
        }
        Dijkstra();
        ans=dist[n];
    }
    cout<<ans<<endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值