玛丽卡

题目描述
麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复。
因为她和他们不住在同一个城市,因此她开始准备她的长途旅行。
在这个国家中每两个城市之间最多只有一条路相通,并且我们知道从一个城市到另一个城市路上所需花费的时间。
麦克在车中无意中听到有一条路正在维修,并且那儿正堵车,但没听清楚到底是哪一条路。无论哪一条路正在维修,从玛丽卡所在的城市都能到达麦克所在的城市。
玛丽卡将只从不堵车的路上通过,并且她将按最短路线行车。麦克希望知道在最糟糕的情况下玛丽卡到达他所在的城市需要多长时间,这样他就能保证他的女朋友离开该城市足够远。
编写程序,帮助麦克找出玛丽卡按最短路线通过不堵车道路到达他所在城市所需的最长时间(用分钟表示)。
输入格式:
第一行有两个用空格隔开的数N和M,分别表示城市的数量以及城市间道路的数量。1≤N≤1000,1≤M≤N*(N-1)/2。城市用数字1至N标识,麦克在城市1中,玛丽卡在城市N中。
接下来的M行中每行包含三个用空格隔开的数A,B和V。其中1≤A,B≤N,1≤V≤1000。这些数字表示在A和城市B中间有一条双行道,并且在V分钟内是就能通过。
输出格式:
输出文件的第一行中写出用分钟表示的最长时间,在这段时间中,无论哪条路在堵车,玛丽卡应该能够到达麦克处,如果少于这个时间的话,则必定存在一条路,该条路一旦堵车,玛丽卡就不能够赶到麦克处。

意思就是最短路但有一条边不能走,看到题,最暴力的想法,枚举删掉每一条边,然后求m-1次最短路取最大值。可这样做无疑是超时的。怎么办?想了10分钟。找了找规律想了想DP发现不具有最优子结构,因此无法DP。想起了昨天cjr神犇教我的次小生成树算法(最小生成树后找不在生成树上的边来替换(替换时不用找每一条生成树边,只需要找生成树上两点之间权值最大边,倍增实现))。对于删掉一条边的最短路,如果删掉的边不影响最优解,那么将不会影响答案。如果删掉的边是最优解上的边,我们才需要重新来找。所以我们可以找出最优解的边,然后一一删去来求最短路最大值。

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e6;

int head[MAXN],cnt=0,dis[MAXN],pre[MAXN],ban[MAXN],ks,js,ans=0;

struct edge{
    int to,next,w;
}e[MAXN<<2];

struct hnd{
    int u,d;
    bool operator<(const hnd&rhs)const{
        return d>rhs.d;
    }
};

priority_queue<hnd>q;
inline void add(int u,int v,int w){e[++cnt]=(edge){v,head[u],w},head[u]=cnt;}

void dij(int u){
    memset(dis,0x7f,sizeof(dis));
    dis[u]=0;q.push((hnd){u,dis[u]});
    while(!q.empty()){
        hnd x=q.top();q.pop();
        int u=x.u;
        if(x.d!=dis[u])continue;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to,w=e[i].w;
            if(dis[u]+w<dis[v]){
                dis[v]=dis[u]+w;
                pre[v]=u;
                q.push((hnd){v,dis[v]});
            }
        }
    }
}

void dijban(int u){
    memset(dis,0x7f,sizeof(dis));
    dis[u]=0;q.push((hnd){u,dis[u]});
    while(!q.empty()){
        hnd x=q.top();q.pop();
        int u=x.u;
        if(x.d!=dis[u])continue;
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to,w=e[i].w;
            if(u==ks&&v==js||v==ks&&u==js)continue;
            if(dis[u]+w<dis[v]){
                dis[v]=dis[u]+w;
                pre[v]=u;
                q.push((hnd){v,dis[v]});
            }
        }
    }
}

int n,m,tem1,tem2,tem3;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&tem1,&tem2,&tem3);
        add(tem1,tem2,tem3);
        add(tem2,tem1,tem3);
    }
    dij(n);
    int times=0;
    ban[++times]=1;
    for(int i=pre[1];i;i=pre[i]){
        ban[++times]=i;
    }
    for(int i=1;i<=times-1;i++){
        ks=ban[i],js=ban[i+1];
    //  printf("%d %d\n",ks,js);
        dijban(n);
        ans=max(ans,dis[1]);
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值