HDU 2433 Travel 最短路应用

题意:有n个点,m条边,求依次删除每一条边后所有两点之间的最短路径的和。

思路:先以每一个顶点为源点求一遍最短路,这样就形成了n个最短路树,第i个树的边权和为sum[i]。然后枚举删除每一个边,分别以边上两点u、v为源点求最短路,如果u、v的距离发生变化,则以u为源点时,v节点下的子树的dis[i]都会变化,因边是双向的,i到u的距离也一样会发生改变(以v为源点时情况是一样的)。那么,以u、v为源点求最短路后生成树的边权和分别为num1和num2,总和的变化量为2×(num1 + num2) - 2×(sum[u] + sum[v]) - 2×(dis[u] - 1) 【因为u到v的边在之前算了4次,本来算2次就够了】

关键点:假设被删除的边出现在以a为源点的最短路树中,b在被删除边的两点中的某一点的子树中,则a也一定在b的最短路树中。


#include<cstdio>
#include<iostream>
#include<queue>
#include<functional>
#include<vector>
#include<algorithm>
#include<stack>
#include<cstring>
#include<cmath>
#include<cstdlib>

using namespace std;
typedef pair<int,int> P;
const int maxn = 105;
const int maxm = 3005;
const int inf = 1e9;

struct Edge{
    int to,cost,tag;
};

int n,m;
vector<Edge> G[maxn];
int dis[maxn],sum[maxn];
int uu[maxm],vv[maxm];
priority_queue<P,vector<P>,greater<P> > que;

void init(){
    memset(sum,0,sizeof(sum));
    for(int i=0;i<maxn;i++) G[i].clear();
}

void add_Edge(int from,int to,int cost,int tag){
    Edge e;
    e.to = to;e.cost = cost;e.tag = tag;
    G[from].push_back(e);
    e.to = from;
    G[to].push_back(e);
}

int dij(int s,int k){
    fill(dis,dis+maxn,inf);
    dis[s] = 0;
    que.push(P(0,s));
    while(!que.empty()){
        P p = que.top();que.pop();
        int v = p.second;
        if(dis[v] != p.first) continue;
        for(int i=0;i<G[v].size();i++){
            Edge e = G[v][i];
            if(e.tag == k) continue;
            if(dis[e.to] > dis[v] + e.cost){
                dis[e.to] = dis[v] + e.cost;
                que.push(P(dis[e.to],e.to));
            }
        }
    }
    int res = 0;
    for(int i=1;i<=n;i++){
        //cout<<i<<" "<<dis[i]<<endl;
        if(dis[i] != inf) res += dis[i];
        else return inf;
    }
    return res;
}


int main(){
    while(~scanf("%d%d",&n,&m)){
        init();
        for(int i=1;i<=m;i++){
            scanf("%d%d",&uu[i],&vv[i]);
            add_Edge(uu[i],vv[i],1,i);
        }
        int ans = 0;
        for(int i=1;i<=n;i++){
            sum[i] = dij(i,0);
            //cout<<sum[i]<<endl;
            if(sum[i] == inf){
                ans = inf;
                break;
            }else ans += sum[i];
        }
        if(ans == inf){
            while(m--) printf("INF\n");
        }else{
            for(int i=1;i<=m;i++){
                int u = uu[i],v = vv[i];
                int num1 = dij(u,i);
                int num2 = dij(v,i);
                //cout<<num1<<" "<<num2<<endl;
                if(num1 == inf){
                    printf("INF\n");
                    continue;
                }else printf("%d\n",ans + 2*num1 + 2*num2 - 2*sum[u] - 2*sum[v] - 2*dis[u] + 2);
            }
        }
    }

    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值