ACdream 1415 最短路+桥的判断

Important Roads

Problem Description

  The city where Georgie lives has n junctions some of which are connected by bidirectional roads.
  Every day Georgie drives from his home to work and back. But the roads in the city where Georgie lives are very bad, so they are very often closed for repair. Georgie noticed that when some roads are closed he still can get from home to work in the same time as if all roads were available.
  But there are such roads that if they are closed for repair the time Georgie needs to get from home to work increases, and sometimes Georgie even cannot get to work by a car any more. Georgie calls such roads important.
  Help Georgie to find all important roads in the city.

Input

  The first line of the input file contains n and m — the number of junctions and roads in the city where Georgie lives, respectively (2 ≤ n ≤ 20 000, 1 ≤ m ≤ 100 000). Georgie lives at the junction 1 and works at the junction n.
  The following m lines contain information about roads. Each road is specified by the junctions it connects and the time Georgie needs to drive along it. The time to drive along the road is positive and doesn’t exceed 100 000. There can be several roads between a pair of junctions, but no road connects a junction to itself. It is guaranteed that if all roads are available, Georgie can get from home to work.

Output

  Output l — the number of important roads — at the first line of the output file. The second line must contain l numbers, the numbers of important roads. Roads are numbered from 1 to m as they are given in the input file.

Sample Input

6 7
1 2 1
2 3 1
2 5 3
1 3 2
3 5 1
2 4 1
5 6 2
Sample Output

2
5 7

原题地址:http://acdream.info/problem?pid=1415


思路:

分三步操作:
1、求每个点到源点的最短距离d。用优先队列优化的Dijkstra算法。
2、根据上一步得出的结果进一步求出所有从源点到终点的最短路包含的边。从终点开始进行普通bfs,对于边(u,v),只要满足d[v] + edge.dis == d[u],那么这条边就是上述要求的边。
3、用tarjan算法思想求出所有桥。

补充:tarjan简述

先看求桥的dfs代码:

int low[MAXN],dfn[MAXN],Index;
int ans[MAXM],cnt;
void dfs(int u,int preID) { //这里的 preID不是一般代码中的父亲结点id,而是上一次搜索过的边的id。原因是题目中说了会有重边-_-。这里被坑了好久。
    low[u]=dfn[u]=++Index;
    for(int e=g2.head[u];e!=-1;e=g2.edge[e].next) {
        int v=g2.edge[e].to, id=g2.edge[e].id;
        if(id==preID) continue;
        if(dfn[v]==0) dfs(v,id);
        low[u]=min(low[u],low[v]);
        if(low[v]>dfn[u]) {
            ans[cnt++]=g2.edge[e].id;
        }
    }
}

dfn[u]记录节点u在DFS过程中被遍历到的次序号,low[u]记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小)。
这里写图片描述
tarjan算法不止可以用在这里求桥,还有其他很多应用。

代码:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN = 20005;//点数的最大值
const int MAXM = 100005*2;//边数的最大值
const int INF = (1<<31)-1;
struct MAP
{
    struct Edge
    {
        int to,next,dis,id;
        bool cut;
    } edge[MAXM];
    int tot;
    int head[MAXN];
    void init()
    {
        tot = 0;
        memset(head,-1,sizeof(head));
    }
    void addedge(int u,int v,int dis,int id)
    {
        edge[tot].to=v;
        edge[tot].dis=dis;
        edge[tot].next=head[u];
        edge[tot].id=id;
        head[u]=tot++;
    }
} g1,g2;
int d[MAXN];
bool vis[MAXN];
struct Node
{
    int u,dis;
    Node(int u,int d):u(u),dis(d) {}
};
bool operator < (Node a,Node b)
{
    return a.dis > b.dis;
}
void createG2(int n)
{
    //求最短路
    memset(vis,0,sizeof(vis));
    priority_queue<Node> que;
    for(int i=0; i<=n; i++) d[i]=INF;
    d[1]=0;
    que.push(Node(1,0));
    while(!que.empty())
    {
        Node tmp = que.top();
        que.pop();
        int u=tmp.u;
        if(vis[u]) continue;
        vis[u]=true;
        for(int e=g1.head[u]; e!=-1; e=g1.edge[e].next)
        {
            int v=g1.edge[e].to,dis=g1.edge[e].dis;
            if(vis[v]) continue;
            if(d[u]+dis<d[v])
            {
                d[v]=d[u]+dis;
                que.push(Node(v,d[v]));
            }
        }
    }
    //建立G2
    g2.init();
    memset(vis,0,sizeof(vis));
    queue<int> q;
    q.push(n);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        if(vis[u]) continue;
        vis[u]=true;
        for(int e=g1.head[u]; e!=-1; e=g1.edge[e].next)
        {
            int v=g1.edge[e].to, dis=g1.edge[e].dis, id=g1.edge[e].id;
            if(d[v]+dis==d[u])
            {
                q.push(v);
                g2.addedge(u,v,0,id);
                g2.addedge(v,u,0,id);
            }
        }
    }
}
int low[MAXN],dfn[MAXN],Index;
int ans[MAXM],cnt;
void dfs(int u,int preID) {
    low[u]=dfn[u]=++Index;
    for(int e=g2.head[u];e!=-1;e=g2.edge[e].next) {
        int v=g2.edge[e].to, id=g2.edge[e].id;
        if(id==preID) continue;
        if(dfn[v]==0) dfs(v,id);
        low[u]=min(low[u],low[v]);
        if(low[v]>dfn[u]) {
            ans[cnt++]=g2.edge[e].id;
        }
    }
}

int main()
{
    int n,m,u,v,dis;
    scanf("%d%d",&n,&m);

    g1.init();
    g2.init();
    for(int i=0; i<m; i++)
    {
        scanf("%d%d%d",&u,&v,&dis);
        g1.addedge(u,v,dis,i+1);
        g1.addedge(v,u,dis,i+1);
    }
    createG2(n);
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    Index=0;
    cnt=0;
    dfs(1,-1);
    sort(ans,ans+cnt);
    printf("%d\n",cnt);
    if(cnt>0)
    {
        for(int i=0; i<cnt; i++)
        {
            if(i==0) printf("%d",ans[i]);
            else printf(" %d",ans[i]);
        }
        puts("");
    }
    return 0;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值