qzezoj 1543 最快路线

题面传送门
看到这道题,想到 s p f a spfa spfa
但这道题好像故意的,偏偏又路牌被拆的道路(在衢州的话负责那一段路的交警就该下台了),
所以我们不能只以 d i d_i di t i m e time time的唯一标准。
想象一下:如果下一条路是没有路牌的,而这时过来一条路, t i m e time time d i d_i di大,但如果下一条路可以走得通怎么办?而且还走得比 d i d_i di快。这时候就进退两难,如果放过去,那 s p f a spfa spfa就废了。
如果不放过去,答案就废了。
想想 f l y i n g flying flying经常说什么? d p dp dp状态表达不完整就再加一维状态!我们可以用 d p dp dp的思路:以 d i , j d_{i,j} di,j表示以 j j j速度开到 i i i点所需最短时间。那么 s p f a spfa spfa还是照样松弛呗。
这道题要求我们输出经过点序,所以我们可以 s p f a spfa spfa的过程中当出现一个最优解时指回去。然后最后倒序输出。
代码实现:

#include<cstdio>
#include<queue>
using namespace std;
int n,m,k,x,y,z,h,zj[100039],head,now1[159][559],now2[159][559],tmp1,tmp2;
double dp[159][559],ans;
struct yyy{
    int a,b,c;
}f[159][159],cur;
struct fs{
    int mu,su;
}tmp;
queue<fs> q; 
int main(){
//  freopen("1.in","r",stdin);
    int i,j;
    scanf("%d%d%d",&n,&m,&k);
    for(i=1;i<=m;i++) scanf("%d%d%d%d",&x,&y,&z,&h),f[x][++f[x][0].a]=(yyy){y,z,h};
    q.push((fs){0,70});
    for(i=0;i<=500;i++) now1[0][i]=-1,now2[0][i]=-1;
    for(i=0;i<=150;i++){
        for(j=0;j<=550;j++) dp[i][j]=100000000;
    }
    dp[0][70]=0;
    while(!q.empty()){
        tmp=q.front();
        q.pop();
        for(i=1;i<=f[tmp.mu][0].a;i++){
            cur=f[tmp.mu][i];
            if(!cur.b){
                ans=dp[tmp.mu][tmp.su]+cur.c*1.0/tmp.su;
                if(ans<dp[cur.a][tmp.su]){
                    dp[cur.a][tmp.su]=ans;
                    now1[cur.a][tmp.su]=tmp.mu;
                    now2[cur.a][tmp.su]=tmp.su;
                    q.push((fs){cur.a,tmp.su});
                }
            }
            else{
                ans=dp[tmp.mu][tmp.su]+cur.c*1.0/cur.b;
                if(ans<dp[cur.a][cur.b]){
                    dp[cur.a][cur.b]=ans;
                    now1[cur.a][cur.b]=tmp.mu;
                    now2[cur.a][cur.b]=tmp.su;
                    q.push((fs){cur.a,cur.b});
                }
            }
        }
    }
    //printf("%d %d %d %d %d %d %d %d\n",now1[1][64],now2[1][64],now1[3][90],now2[3][90],now1[2][90],now2[2][90],now1[5][70],now2[5][70]);
    ans=1000000000;
    for(i=1;i<=550;i++){
        if(dp[k][i]>0) ans=min(ans,dp[k][i]);
    }
    for(i=1;i<=550;i++){
        if(dp[k][i]==ans){
            tmp1=now1[k][i];tmp2=now2[k][i];
            printf("0");
            while(tmp1){
                zj[++head]=tmp1;
                h=tmp1;x=tmp2;
                tmp1=now1[h][x];tmp2=now2[h][x];
            }
            for(j=head;j>=1;j--) printf(" %d",zj[j]);
            printf(" %d",k);
        }
    }
}

然而又要卡常…可能这道题有卡 s p f a spfa spfa的数据吧…
加寄存器,手写队列…又是卡过去的… 966 m s 966ms 966ms。本机都跑的飞快呀…
代码实现:

#include<cstdio>
#include<queue>
using namespace std;
int zj[1039],now1[159][559],now2[159][559];
double dp[159][559],ans;
struct yyy{
    int a,b,c;
}f[159][159];
struct fs{
    int mu,su;
}q[500039];
inline void read(int &x){
    x=0;char s=getchar();
    while(s<'0'||s>'9') s=getchar();
    while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
int main(){
//  freopen("fast.in","r",stdin);
//  freopen("fast.out","w",stdout);
    register int i,j,he=0,ta=0,cura,curb,tmpsu,tmpmu,curc,n,m,k,x,y,z,h,head=0,tmp1,tmp2;
    scanf("%d%d%d",&n,&m,&k);
    for(i=1;i<=m;i++) read(x),read(y),read(z),read(h),f[x][++f[x][0].a]=(yyy){y,z,h};
    q[++ta]=((fs){0,70});
    for(i=0;i<=500;i++) now1[0][i]=-1,now2[0][i]=-1;
    for(i=0;i<=n;i++){
        for(j=0;j<=500;j++) dp[i][j]=1000000000;
    }
    dp[0][70]=0;
    while(he!=ta){
        tmpsu=q[he+1].su;tmpmu=q[he+1].mu;
        he++;
        for(i=f[tmpmu][0].a;i>=1;i--){
            cura=f[tmpmu][i].a;curb=f[tmpmu][i].b;curc=f[tmpmu][i].c;
            if(!curb){
                ans=dp[tmpmu][tmpsu]+curc*1.0/tmpsu;
                if(ans<dp[cura][tmpsu]){
                    dp[cura][tmpsu]=ans;
                    now1[cura][tmpsu]=tmpmu;
                    now2[cura][tmpsu]=tmpsu;
                    q[++ta]=((fs){cura,tmpsu});
                }
            }
            else{
                ans=dp[tmpmu][tmpsu]+curc*1.0/curb;
                if(ans<dp[cura][curb]){
                    dp[cura][curb]=ans;
                    now1[cura][curb]=tmpmu;
                    now2[cura][curb]=tmpsu;
                    q[++ta]=((fs){cura,curb});
                }
            }
        }
    }
    //printf("%d %d %d %d %d %d %d %d\n",now1[1][64],now2[1][64],now1[3][90],now2[3][90],now1[2][90],now2[2][90],now1[5][70],now2[5][70]);
    ans=1000000000;
    for(i=1;i<=500;i++){
        if(dp[k][i]>0) ans=min(ans,dp[k][i]);
    }
    for(i=1;i<=500;i++){
        if(dp[k][i]==ans){
            tmp1=now1[k][i];tmp2=now2[k][i];
            while(tmp2!=-1&&tmp1!=-1){
                zj[++head]=tmp1;
                h=tmp1;
                tmp1=now1[h][tmp2];tmp2=now2[h][tmp2];
            }
            for(j=head;j>=1;j--) printf("%d ",zj[j]);
            printf("%d ",k);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值