最短路+字典序最小+输出路径(Ideal Path,UVA 1599)

题目链接:

https://vjudge.net/problem/UVA-1599


在满足最短路的条件下,字典序最小,输出最短距离以及路径。


一些思考:

紫书上说可以只用一次BFS来完成,但是自己没有什么好的想法,而且网上也没有找到相关资料,而且两次BFS和一次BFS差距不大,所以就两次吧。


如果只是单纯的求最短路,那么直接对源点跑单源最短路算法:BFS或者Dijkstra即可。

输出具体路径的方式也很简单,就是维护一个p数组,然后逆序输出就好了。(因为逆序输出通过贪心能保证到达源点,而顺序输出通过贪心不一定能走到汇点,可能会走到一些死胡同。)


最短路长短是跟距离有关的性质,而字典序大小是跟节点数有关的性质,如果距离和节点数之间没有什么关系,比如说带权图,那么我们只能先用最短路算法预处理出最短路树,然后再在这个最短路树上考虑字典序大小的问题(考虑节点数相关问题),可以用BFS来解决,具体办法下面会说。

如果这是一个无权图,那么最短路长短和字典序大小就可以通过BFS一次性计算出来。


如果在满足距离最短的前提下,还要求字典序最小,那么我们首先得保证这是一个源点到汇点最短路,其次得保证这个源点到汇点的最短路在所有源点到汇点的最短路中的字典序是最小的。


可见我们考虑的范围是源点到汇点的所有最短路,我们必须不重不漏地把这些路径找到,如果找少了,当然不行,如果找多了,那么可能会影响我们求最小字典序。


怎么找呢?

我们都知道,单最短路算法是求源点到任意点的最短距离和路径,其中自然包含从源点到汇点的一条或多条路径,也包含很多通往其他节点的路径,这些无用的路径我们应该剔除,以免干扰我们判断字典序。

同理,单最短路算法是求任意点到汇点的最短距离和路径,其中自然包含从源点到汇点的一条或多条路径,也包含很多来自其他节点的路径,这些无用的路径我们也应该剔除,以免干扰我们判断字典序。

这两个算法得到的最短路树求一个交集就是我们要的答案。

但是事实上我们不用那么麻烦,我们只用跑其中一个算法,然后对另一端跑BFS,标记可到达的最短路树上的节点即可。


比如:

首先我们对源点跑一遍单源最短路算法,得到源点到任意点的最短距离和路径。

然后我们反向建图,对汇点跑一遍BFS,沿途标记所有经过的最短路树上的节点。

所有被标记过的节点一定能从源点到达,也一定能到达汇点,并且一定是在最短路树上。

这样这些被标记过的节点就是我们需要的节点了。

最后就是要在这些从源点到汇点的最短路径中找到字典序最小的那个了。

我们从源点出发,对被标记过的节点跑一遍BFS,对每一层次的BFS,我们在扩展出下一层次时,应当及时剔除那些字典序一定更大的节点,保留剩下的作为下一层次的种子,并沿途记录下最小的字典序,直到结束。最后输出沿途记录的答案即可。


事实上我们可以做的更好:

如果我们对汇点跑一遍单汇最短路算法,接下来我们再对源点跑一个BFS,我们就可以在标记节点的同时,完成寻找最小字典序路径的任务了。


单汇最短路:

对原图反向建图后跑单源最短路,得到的就是单汇最短路。

具体原理和思路大同小异,都是取最小的节点来扩展,可以保证是最优的,思路还是有所不同,一个是从源点出发,另一个是在汇点终止,只不过代码刚好长一样罢了。



对于无向图,可以省略反向建图的过程。

对于网格形状的图,我们甚至可以在第三次BFS采用贪心策略来代替第二次的BFS。

比如:https://vjudge.net/problem/HDU-5335

我的理解:http://blog.csdn.net/xl2015190026/article/details/72874427


代码

#include<stdio.h>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 100010;

int n,m;
vector<int>G[maxn];
vector<int>C[maxn];
vector<int>ans;
int d[maxn];
int vis[maxn];

void init()
{
    for(int i=1;i<=n;i++) G[i].clear(),C[i].clear(),d[i]=-1,vis[i]=0;;
    ans.clear();
}

void read()
{
    int u,v,c;
    for(int i=0;i<m;i++)
    {
        scanf("%d %d %d",&u,&v,&c);
        G[u].push_back(v);
        C[u].push_back(c);
        G[v].push_back(u);
        C[v].push_back(c);
    }
}

void invbfs()
{
    queue<int>Q;
    Q.push(n);
    d[n]=0;
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(int i=0;i<(int)G[u].size();i++)
        {
            int v = G[u][i];
            if(d[v]!=-1) continue;
            d[v]=d[u]+1;
            Q.push(v);
        }
    }
}

void bfs()
{
    queue<int>Q;
    Q.push(1);
    vis[1]=1;
    while(!Q.empty())
    {
        vector<int>vec;
        while(!Q.empty())
        {
            vec.push_back(Q.front());
            Q.pop();
        }
        int MIN = 1e9+10;
        for(int i=0;i<(int)vec.size();i++)
        {
            int u = vec[i];
            for(int j=0;j<(int)G[u].size();j++)
            {
                int v = G[u][j];
                if(d[v]+1==d[u]) MIN=min(MIN,C[u][j]);
            }
        }

        if(MIN==1e9+10) return;
        ans.push_back(MIN);

        for(int i=0;i<(int)vec.size();i++)
        {
            int u = vec[i];
            for(int j=0;j<(int)G[u].size();j++)
            {
                int v = G[u][j];
                if(d[v]+1==d[u]&&C[u][j]==MIN&&!vis[v]) Q.push(v),vis[v]=1;
            }
        }
    }
}

int print()
{
    printf("%d\n",ans.size());
    for(int i=0;i<(int)ans.size();i++) printf("%d%c",ans[i],i+1==(int)ans.size()?'\n':' ');
    return 0;
}

int solve()
{
    init();
    read();
    invbfs();
    bfs();
    return 0*print();
}

int main()
{
    while(scanf("%d %d",&n,&m)==2) solve();
    return 0;
}




  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值