hdu 1595 find the longest of the shortest (dijkstra + spfa)


find the longest of the shortest


题目链接:Problem - 1595

题目大意:现有一个无向图,其中某一条边不存在(要删除),求所有情况中最短路里的最大值。

题目分析:

思路一:枚举m条边,一一删除进行spfa求出最短路,然后比较出最大值。(超时)

思路二:先利用dijkstra求出最短路,再在最短路里枚举个边,一一删除进行spfa求出最短路,然后比较最大值。(AC)

思路分析:之所以能在最短路里删边求最小值,避免了一一枚举m条边,是因为删边后最短路要保持其值小,如果删除最短路意外的边,所有最小值都等于最短路的值,就不存在最大值了,所以利用原来最短路的基础上删边是优化的正确选择。


步骤: 1)存图  vector

2)找最短路 dijkstra

3)路径还原 (前驱)

4)在最短路中一一删边,每次求出最短路,比较出最大值(spfa)


贴AC代码:

#include <stdio.h> //定义输入/输出函数
#include <limits.h> //定义各种数据类型最值常量
#include <math.h> //定义数学函数
#include <stdlib.h> //定义杂项函数及内存分配函数
#include <string.h> //字符串处理
#include <algorithm>//算法
#include <queue>//队列
#include <stack>//栈
#include <vector>
using namespace std;
int n, m;
int vis[1005];
int road[1005];   //记录路径
int a, b, t, couts, _a, _b, k;
unsigned int ans, maxs;
struct qq{
    int to, cost;
};
qq input;
vector <qq> v[1005];    //存图
struct node{
    int cost, pre;
};
node low[1005]; //dijkstra用
struct pp{
    int loc, cost;
    friend bool operator < (pp a, pp b)
    {
        return a.cost > b.cost;
    }
};      //spfa优先队列用
void dijkstra()   //求最短路径并还原路径  wa在此处3次
{
    for (int i = 1; i <= n; i++){
        low[i].cost = 10000005;
        low[i].pre = 0;
    }
    memset(vis, 0, sizeof(vis));
    low[1].cost = low[1].pre = 0;
    vis[1] = 1;
    for (int i = 0; i < v[1].size(); i++){
        low[v[1][i].to].cost = v[1][i].cost;
        low[v[1][i].to].pre = 1;
    }
    for (int j = 1; j < n; j++){
        unsigned int mins = -1;
        int loc = 1;
        for (int i = 1; i <= n; i++){
            if (low[i].cost && low[i].cost < mins && vis[i] == 0){
                mins = low[i].cost;
                loc = i;
            }
        }       //找到最小值位置
        vis[loc] = 1;
        for (int i = 0; i < v[loc].size(); i++){
            if (vis[v[loc][i].to] == 1) continue;
            // 如果满足更新后的距离小于原来的值,则可以更新,并更新前驱
            if (low[v[loc][i].to].cost > low[loc].cost + v[loc][i].cost){
                low[v[loc][i].to].cost = low[loc].cost + v[loc][i].cost;
                low[v[loc][i].to].pre = loc;
            }
        }
    }
    //路径还原
    k = n;
    couts = 0;
    road[couts++] = k;
    while(k != 0){
        road[couts++] = low[k].pre;
        k = low[k].pre;
    }
}
void spfa()     //求最短路的spfa  被题目绕进去了一开始求了最大值,wa了一次
{
    priority_queue <pp> q;
    while (!q.empty()) q.pop();
    memset(vis, 0, sizeof(vis));
    pp start;
    start.loc = 1;
    start.cost = 0;
    q.push(start);
    while (!q.empty()){
        pp news, tmp;
        tmp = q.top();
        q.pop();
        vis[tmp.loc] += 1;
        if (tmp.loc == n){
            if (ans > tmp.cost)
                ans = tmp.cost;
            break;
        }           //要求出每次删边的最短路!
        for (int i = 0; i < v[tmp.loc].size(); i++){
            news = tmp;
            if (vis[v[tmp.loc][i].to] == 1) continue;
            if (vis[tmp.loc] > 1) continue;
            //删边
            if (tmp.loc == _a && v[tmp.loc][i].to == _b) continue;
            if (v[tmp.loc][i].to == _a && tmp.loc == _b) continue;
            news.loc = v[tmp.loc][i].to;
            news.cost += v[tmp.loc][i].cost;
            //printf("%d -> %d , cost %d\n", tmp.loc, news.loc, news.cost);
            q.push(news);
        }
    }
}
int main()
{
    while (scanf("%d", &n) != EOF && n){
        scanf("%d", &m);
        for (int i = 0; i < 1005; i++)
            v[i].clear();
        for (int i = 0; i < m; i++){
            scanf("%d%d%d", &a, &b, &t);
            input.to = b;
            input.cost = t;
            v[a].push_back(input);
            input.to = a;
            v[b].push_back(input);
        }
        dijkstra();
        maxs = 0;
        for (int i = 0; i < couts - 1; i++){
            //printf(" -> %d ", road[i]);
            _a = road[i];
            _b = road[i+1];
            ans = -1;
            spfa();
            if (maxs < ans)
                maxs = ans;    //在每次求出的最短路中找出最大值
        }
        printf("%d\n", maxs);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值