UVA 1416 最短路

UVA 1416

题目链接:

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&category=447&problem=4162&mosmsg=Submission+received+with+ID+16031798

题意:

给一个图n个点,m条边,n <= 100m <= 1000

问两个值。第一个值是原图中每个点到其余点最短路的和。

第二个值是删除一条边之后,每个点到其余点最短路和的最大值。

思路:

看了题解打的。

第一个值的思路很简单,对每个点做一次原始的Dijkstra

第二个值就比较麻烦了。题解采用一种标记边的方法。如果当前删除边在原最短路中,则重新做一次dijkstra。否则直接用原来值就行。

然而并不知道怎么实现。上网查别人代码发现一种最优美的姿势存一个use[s][cnt]是对一个起点s,把所有在dis[v]<dis[s]+val中遍历过的边都标记use[s][edge_num]=1

然而WA点出现了。自己写的时候如果删除边在最短路中,则更新这个点的sum[s]值(最短路路径和)然而这样是错的。因为更新后不能保证他是最短路了。所以dijkstra时要分两种情况讨论。一种要更新use数组,一种则不要。

源码:

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <cmath>

#include <string>

#include <algorithm>

#include <iostream>

#include <queue>

#include <vector>

using namespace std;

#define N 1005

#define LL long long

#define inf (1000000000000)

int n, m;

LL L;

int head[N], cnt;

struct Edge

{

    int v, ne;

    LL val;

    Edge(){}

    Edge(int _u, int _v, LL _val){v = _v, val = _val, ne = head[_u];}

}edge[N * 2];

void add_edge(int u, int v, LL val)

{

    edge[cnt] = Edge(u, v, val);

    head[u] = cnt++;

}

struct D

{

    int d, u;

    bool operator < (const D &rbs)const

    {

        return d > rbs.d;

    }

};

priority_queue<D>que;

int use[N][N * 2];

LL dis[N][N];

int vis[N];

LL sum[N];

LL sum1, sum2;

int del[N*2];

LL dijkstra(int s, int op)

{

    LL ans = 0;

    for(int i = 1 ; i <= n ; i++)

        dis[s][i] = inf, vis[i] = 0;

    if(op == 0)

        memset(use[s], 0, sizeof(use[s]));

    dis[s][s] = 0;

    while(!que.empty()) que.pop();

    que.push(D{0, s});

    while(!que.empty()){

        int org = que.top().u;  que.pop();

        if(vis[org])    continue;

        vis[org] = 1;

        for(int now = head[org] ; now != -1 ; now = edge[now].ne){

            int v = edge[now].v;

            if(del[now])    continue;

            if(dis[s][v] > dis[s][org] + edge[now].val){

                dis[s][v] = dis[s][org] + edge[now].val;

                if(op == 0)

                    use[s][now] = use[s][now^1] = 1;

                que.push(D{dis[s][v], v});

            }

        }

    }

    for(int i = 1 ; i <= n ; i++){

        if(dis[s][i] < inf)

            ans += dis[s][i];

        else

            ans += L;

    }

    return ans;

}

int main()

{

//    freopen("UVA 1416.in", "r", stdin);

    while(scanf("%d%d%lld", &n, &m, &L) != EOF){

        int u, v;

        LL val;

        cnt = 0;

        memset(head, -1, sizeof(head));

        for(int i = 1 ; i <= m ; i++){

            scanf("%d%d%lld", &u, &v, &val);

            add_edge(u, v, val);

            add_edge(v, u, val);

        }

        sum1 = sum2 = 0;

        for(int i = 1 ; i <= n ; i++)

            sum[i] = dijkstra(i, 0), sum1 += sum[i];

        sum2 = 0;

        memset(del, 0, sizeof(del));

        for(int i = 0 ; i < m ; i++){

            LL temp = 0;

            for(int j = 1 ; j <= n ; j++){

                if(use[j][i*2]){

                    del[i*2] = del[(i*2)^1] = 1;

                    temp += dijkstra(j, 1);

                    del[i*2] = del[(i*2)^1] = 0;

                }

                else

                    temp += sum[j];

            }

            sum2 = max(sum2, temp);

        }

        printf("%lld %lld\n", sum1, sum2);

    }

    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值