SDOI2009 Elaxia的路线(最短路+拓扑排序)

题目描述

最近,Elaxia和w的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们 必须合理地安排两个人在一起的时间。Elaxia和w每天都要奔波于宿舍和实验室之间,他们 希望在节约时间的前提下,一起走的时间尽可能的长。 现在已知的是Elaxia和w**所在的宿舍和实验室的编号以及学校的地图:地图上有N个路 口,M条路,经过每条路都需要一定的时间。 具体地说,就是要求无向图中,两对点间最短路的最长公共路径。

输入输出格式

输入格式:

第一行:两个整数N和M(含义如题目描述)。 第二行:四个整数x1、y1、x2、y2(1 ≤ x1 ≤ N,1 ≤ y1 ≤ N,1 ≤ x2 ≤ N,1 ≤ ≤ N),分别表示Elaxia的宿舍和实验室及w**的宿舍和实验室的标号(两对点分别 x1,y1和x2,y2)。 接下来M行:每行三个整数,u、v、l(1 ≤ u ≤ N,1 ≤ v ≤ N,1 ≤ l ≤ 10000),表 u和v之间有一条路,经过这条路所需要的时间为l。

输出格式:

一行,一个整数,表示每天两人在一起的时间(即最长公共路径的长度)

输入输出样例

输入样例#1:

9 10
1 6 7 8
1 2 1
2 5 2
2 3 3
3 4 2
3 9 5
4 5 3
4 6 4
4 7 2
5 8 1
7 9 1

输出样例#1:

3

说明

对于30%的数据,N ≤ 100;

对于60%的数据,N ≤ 1000;

对于100%的数据,N ≤ 1500,输入数据保证没有重边和自环。

思路

这是一道山东省选题,虽然很容易看出就是一道最短路的题目  但是还是不会做

一开始以为要两次最短路来实现但是写了一下还是写不出来啊…

只好借鉴了一下另一位Dalao的题解了,他的想法如下:

先把以四个点为起点的SPFA跑一遍,然后建新图

只保留Elaxia的最短路(边权为0)和Elaxia、w的公共最短路。

这里怎么判断某边是否是最短路呢?答案是:dis(s,i)+len(i,j)+dis(j,t)=dis(s,t)

下一步可以用拓扑排序递推:f[b[i].t]=max(f[b[i].t],f[temp]+b[i].v*b[i].ok)

最后输出Elaxia终点的f值。

代码(其实还是很好实现的)

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
    int u,v,w,nxt,f;
}a[2000005],b[2000005];
queue<int>q;
int n,m,cnt,ct2;
int h[2005],p[5],d[5][2005],h2[2005],r[2005],f[2005];//这里其实1505就行
void add(int u,int v,int w)
{
    a[++cnt].u=u;
    a[cnt].v=v;
    a[cnt].w=w;
    a[cnt].nxt=h[u]; 
    h[u]=cnt;
}
void SPFA(int x)//就是正常的SPFA
{    
    bool v[1505]={0};
    for(int i=1;i<=n;++i) 
    if(i!=p[x]) d[x][i]=0x3f3f3f3f;
    q.push(p[x]);
    v[p[x]]=1;
    while( !q.empty() )
    {
        int now=q.front();
        q.pop(),v[now]=0;
        for(int i=h[now];i;i=a[i].nxt)
        {
            int to=a[i].v;
            if(d[x][to]>d[x][now]+a[i].w)
            {
                d[x][to]=d[x][now]+a[i].w;
                if(!v[to]) v[to]=1,q.push(to);
            } 
        }            
    }
}
int main()
{
    int x,y,z;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=4;++i) scanf("%d",&p[i]);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z),add(y,x,z);
    }
    for(int i=1;i<=4;++i) SPFA(i);


    for(int i=1;i<=cnt;++i) //以Elaxia为主重建图 
     if(d[1][a[i].u]+a[i].w+d[2][a[i].v]==d[1][p[2]])
     {
         b[++ct2].u==a[i].u; 
         b[ct2].v=a[i].v; 
         b[ct2].w=a[i].w; 
         b[ct2].nxt=h2[a[i].u]; 
         h2[a[i].u]=ct2;
         if(d[3][a[i].u]+a[i].w+d[4][a[i].v]==d[3][p[4]]||d[4][a[i].u]+a[i].w+d[3][a[i].v]==d[3][p[4]]) b[ct2].f=1;
         r[a[i].v]++;
     }

    q.push(p[1]);
    int now;
    while(!q.empty())//拓扑排序找关键路径 
    {
        now=q.front(); q.pop();
        for(int i=h2[now];i;i=b[i].nxt)
        {
            --r[b[i].v];
            if(!r[b[i].v])
            {
                q.push(b[i].v);
                f[b[i].v]=max(f[b[i].v],f[now]+b[i].w*b[i].f);
            }
        }
    }
    printf("%d",f[p[2]]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值