2019百度之星初赛三 最短路(2)

题目链接:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=863&pid=1002

小 A 是社团里的工具人,有一天他的朋友给了他一个 n 个点,m 条边的正权连通无向图,要他计算所有点两两之间的最短路。

作为一个工具人,小 A 熟练掌握着 floyd 算法,设 w[i][j] 为原图中 (i,j) 之间的权值最小的边的权值,若没有边则 w[i][j]=无穷大。特别地,若 i=j,则 w[i][j]=0。

Floyd 的 C++ 实现如下:

for(int k=1;k<=p;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
    w[i][j]=min(w[i][j],w[i][k]+w[k][j]);

当 p=n 时,该代码就是我们所熟知的 floyd,然而小 A 为了让代码跑的更快点,所以想减少 p 的值。

令 D​i,j​​ 为最小的非负整数 x,满足当 p=x 时,点 i 与点  j 之间的最短路被正确计算了。

现在你需要求 \sum_{i=1}^{n}\sum_{j=1}^{n}D_{i,j}​​,虽然答案不会很大,但为了显得本题像个计数题,你还是需要将答案对 998244353 取模后输出。

 

题意:

一张无向图,n个点m条边。

As we all know, 求两点间最短路时,需要进行松弛操作

把(有效的)松弛操作过程中涉及的下标最大的点记为p,

要正确计算这两点间的最短路,p最小是多少

(如果u和v之间的最短路就是edge(u,v),则p为0(不需要松弛)其他情况 )

答案一定是 u 到 v 的最短路上(不算端点)编号最大的那个节点,根据Floyed算法流程容易证明;如果最短路有很多条呢,那么就取每个最大值的最小值;跑N遍迪杰斯特拉,不断更新Du,v即可;假设现在走 x 到 v 这条边,如果dist[u][v]被更新,那么(1)x != u 时:D[u][v] = max(D[u][x],x),(2)x == u时:D[u][v] = D[u][x](因为端点不能算)

 

思路:

对每个点跑一边Dijkstra,在松弛操作里操作一番。

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int>PII;//这里的路径权值忘开ll 一直WA 
const int N=1005,M=4005,MOD=998244353;

int n,m;
int h[N],e[M],ne[M],idx;
ll w[M];
ll dist[N];
bool st[N];
int cnt[N][N];
ll res=0;

void add(int a, int b, ll c){
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}

void Dij(int s){

    memset(st,0,sizeof st);
    memset(dist,0x3f,sizeof dist);
    
    dist[s]=0;
    
    priority_queue<PII,vector<PII>,greater<PII> >heap;
    heap.push({0,s});

    while(heap.size()){
        auto t=heap.top();
        heap.pop();

        int ver=t.second;
        ll distance=t.first;

        if(st[ver]) continue;
        st[ver]=true;

        for(int i=h[ver];~i;i=ne[i]){
            int j=e[i];
            if(w[i]+distance<dist[j]){//w[i]:ver-j边的权值; distance:s-ver最短路长 
                dist[j]=w[i]+distance;
                heap.push({dist[j],j});
                
                if(dist[j]==w[i]) cnt[s][j]=0;//如果s-j的最短路就是它们之间的那条边(s-j的最短路长dist[j]等于ver-j的权值 也即ver==s ) 不需要枚举 
                else cnt[s][j]=max(ver,cnt[s][ver]);
                
            }else if(w[i]+distance==dist[j]){//多条最短路 
            	heap.push({dist[j],j});//cnt更新了 还是要入队
            	cnt[s][j]=min(cnt[s][j],max(cnt[s][ver],ver));
			}
            
        }
    }

}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        
        memset(h,-1,sizeof h);
        idx=0;
        
        memset(cnt,0,sizeof cnt);
        res=0;

        for(int i=0;i<m;++i){
            int a,b;
            ll c;
            scanf("%d%d%I64d",&a,&b,&c);
            add(a,b,c);
            add(b,a,c);
        }

        for(int i=1;i<=n;++i){
            Dij(i);
        }
        
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                res=(res+cnt[i][j])%MOD;
            }
        }
        
        printf("%I64d\n",res);
    }
}

注意:

1. 稀疏图,用邻接矩阵会T

2. 答案是最短路上的下标的最大值,但不是某轮松弛操作的最大值,有可能那个点被其他下标更大的点更新过!想清楚最短路构成 不断更新的轮数 像递推和BFS似的

2. 为什么不能cal cnt 时,判断 cnt[i][j]!=i && cnt[i][j]!=j 则累加答案? 因为有可能某最短路经过一系列比起点小的点更新了答案 起点是当中最大的 还真就是正确答案 我们排除的是起点到终点就一条边的情况 也只能在松弛操作里去判断 处理cnt

3. 为什么else里面不用判断起点? 因为起点是第一个入队的,不可能进入==的else的情况

4.当cnt[s][j]被更新 就要入队 即便最短路长相等

 

参考博客:

百度之星初赛三 最短路2(Dijkstra)

2019年百度之星 - 初赛三B:最短路2【Dijkstra】

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值