团体程序设计天梯赛-练习集 -- L3-014. 周游世界(dijkstra)

题意:

给你一些边,每个边都属于一个确定的公司,告诉你q 个询问,每个询问让你输出两点之间的最短路,并且打印解,连续属于一个公司的线路,只打印收尾。

思路:

这个题赛场上过样例后,只得到了一分。= =

其实就是个很水的dijkstra。

只不过要求的最通畅路线是 要求节点数最小,节点数相同要求换公司线路最小。

这个题wa了好久,原因是 优先队列优先级写反了。

我们只需要简单的修改一下dijkstra即可。

在保证结点最小的情况下,开个新变量记录这个点的换线次数。 那么结点相同在判断 换线次数最小即可。

把路径记录下后 处理路径打印即可。

结点最小 可以让所有边权值都为1.

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <queue>
using namespace std;
int cnt= 0 ;
map<int,int>mp;


const int inf = 0x3f3f3f3f;

const int maxn = 10000 + 10;
int fid[maxn];
int ID(int x){
    if (mp.count(x)){
        return mp[x];
    }

    fid[cnt] = x;
    return mp[x] = cnt++;
}
int com[maxn][maxn];

struct Edge{
    int f,t,w;
    Edge(int f =0,int t = 0,int w = 0):f(f),t(t),w(w){}
};

struct Node{
    int u,d;///d 是距离。
    int sum;/// sum 是换线总次数
    int ori;///ori 目前所在的公司
    bool operator < (const Node& rhs) const {
        return d > rhs.d || (d == rhs.d && sum > rhs.sum);
    }
    Node(int u = 0,int d = 0,int sum = 0,int ori = -1):u(u),d(d),sum(sum),ori(ori){}
};


struct DDDDD{
    int n,m;
    vector<Edge> edges;
    vector<int>g[maxn];
    priority_queue<Node>q;
    bool vis[maxn];
    int d[maxn];
    int p[maxn];
    int dsum[maxn];

    void init(int xx){
        this->n = xx;
        for (int i = 0; i <= n; ++i) g[i].clear();
        edges.clear();
        m = 0;
        while(!q.empty())q.pop();
    }

    void add(int f,int t,int w){
        edges.push_back(Edge(f,t,w));
        g[f].push_back(m++);
    }

    int dij(int s,int t){
        for (int i = 0; i <= n; ++i)d[i] = inf;
        memset(vis,0,sizeof vis);
        memset(p,-1,sizeof p);
        memset(dsum,inf,sizeof dsum);
        d[s] = 0;
        dsum[s] = 0;
        q.push(Node(s,0));
        while(!q.empty()){
            Node nod = q.top(); q.pop();

            int cur = nod.u;
            if (vis[cur]) continue;
            vis[cur] = 1;

            for (int i = 0; i < g[cur].size(); ++i){
                int v = g[cur][i];
                Edge& e = edges[v ];
                if (d[e.t] > d[cur] + e.w){
                    d[e.t] = d[cur] + e.w;
                    int nxsum = nod.sum,nxori = nod.ori;
                    if (com[e.t ][cur] != nod.ori){
                        nxori = com[e.t][cur];
                        nxsum++;
                    }
                    p[e.t] = cur;
                    dsum[e.t] = nxsum;
                    q.push(Node(e.t,d[e.t],nxsum,nxori));
                }
                else if (d[e.t] == d[cur] + e.w){
                    int nxsum = nod.sum,nxori = nod.ori;
                    if (com[e.t ][cur] != nod.ori){
                        nxori = com[e.t][cur];
                        nxsum++;
                    }
                    if (dsum[e.t] > nxsum){
                        dsum[e.t] = nxsum;
                        p[e.t] = cur;
                        q.push(Node(e.t,d[e.t],nxsum,nxori));
                    }


                }
            }
        }
        return d[t];
    }



}dic;
vector<int>ans;

int main(){




    int n;
    scanf("%d", &n);
    dic.init(maxn);
    for (int i = 1; i <= n; ++i){
        int x,la,num;
        scanf("%d",&num); scanf("%d",&la);
        for (int j = 1; j < num; ++j){
            scanf("%d",&x);
            int idla = ID(la);
            int idx = ID(x);
            dic.add(idla,idx,1);
            dic.add(idx,idla,1);
            com[idla][idx] = com[idx][idla] = i;
            la = x;
        }

    }
    int q;
    scanf("%d",&q);
    while(q--){
        int u,v;
        scanf("%d %d",&u, &v);
        int idu = ID(u), idv = ID(v);
        int vv = dic.dij(idu,idv);
        int cur = idv;
        ans.clear();
        while(cur != -1){
            ans.push_back(cur);
            cur = dic.p[cur];

        }
        if ((int)ans.size() <= 1){
            puts("Sorry, no line is available.");
            continue;
        }

        reverse(ans.begin(),ans.end());
        cur = com[ans[0] ][ans[1] ];


        ans.push_back(0);

        int len = ans.size();
        printf("%d\n",len-2);
        int s = 0;


        for (int i = 1; i < len; ++i){
            if (com[ans[i-1] ][ans[i] ] != cur){
                printf("Go by the line of company #%d from %.4d to %.4d.\n",cur,fid[ans[s] ], fid[ans[i-1] ]);
                s = i-1;
                cur = com[ans[i-1] ][ans[i] ];
            }
            else continue;
        }




    }



    return 0;
}



L3-014. 周游世界

时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
陈越

周游世界是件浪漫事,但规划旅行路线就不一定了…… 全世界有成千上万条航线、铁路线、大巴线,令人眼花缭乱。所以旅行社会选择部分运输公司组成联盟,每家公司提供一条线路,然后帮助客户规划由联盟内企业支持的旅行路线。本题就要求你帮旅行社实现一个自动规划路线的程序,使得对任何给定的起点和终点,可以找出最顺畅的路线。所谓“最顺畅”,首先是指中途经停站最少;如果经停站一样多,则取需要换乘线路次数最少的路线。

输入格式:

输入在第一行给出一个正整数N(<= 100),即联盟公司的数量。接下来有N行,第i行(i=1, ..., N)描述了第i家公司所提供的线路。格式为:

M S[1] S[2] ... S[M]

其中M(<= 100)是经停站的数量,S[i](i=1, ..., M)是经停站的编号(由4位0-9的数字组成)。这里假设每条线路都是简单的一条可以双向运行的链路,并且输入保证是按照正确的经停顺序给出的 —— 也就是说,任意一对相邻的S[i]和S[i+1](i=1, ..., M-1)之间都不存在其他经停站点。我们称相邻站点之间的线路为一个运营区间,每个运营区间只承包给一家公司。环线是有可能存在的,但不会不经停任何中间站点就从出发地回到出发地。当然,不同公司的线路是可能在某些站点有交叉的,这些站点就是客户的换乘点,我们假设任意换乘点涉及的不同公司的线路都不超过5条。

在描述了联盟线路之后,题目将给出一个正整数K(<= 10),随后K行,每行给出一位客户的需求,即始发地的编号和目的地的编号,中间以一空格分隔。

输出格式:

处理每一位客户的需求。如果没有现成的线路可以使其到达目的地,就在一行中输出“Sorry, no line is available.”;如果目的地可达,则首先在一行中输出最顺畅路线的经停站数量(始发地和目的地不包括在内),然后按下列格式给出旅行路线:

Go by the line of company #X1 from S1 to S2.
Go by the line of company #X2 from S2 to S3.
......

其中Xi是线路承包公司的编号,Si是经停站的编号。但必须只输出始发地、换乘点和目的地,不能输出中间的经停站。题目保证满足要求的路线是唯一的。

输入样例:
4
7 1001 3212 1003 1204 1005 1306 7797
9 9988 2333 1204 2006 2005 2004 2003 2302 2001
13 3011 3812 3013 3001 1306 3003 2333 3066 3212 3008 2302 3010 3011
4 6666 8432 4011 1306
4
3011 3013
6666 2001
2004 3001
2222 6666
输出样例:
2
Go by the line of company #3 from 3011 to 3013.
10
Go by the line of company #4 from 6666 to 1306.
Go by the line of company #3 from 1306 to 2302.
Go by the line of company #2 from 2302 to 2001.
6
Go by the line of company #2 from 2004 to 1204.
Go by the line of company #1 from 1204 to 1306.
Go by the line of company #3 from 1306 to 3001.
Sorry, no line is available.

提交代码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Dijkstra算法是一种用于解决单源最短路径问题的算法。它的基本思想是从起点开始,逐步扩展到其他节点,每次选择当前距离起点最近的节点,并更新与该节点相邻的节点的距离。通过这种方式,可以找到起点到其他节点的最短路径。Dijkstra算法的时间复杂度为O(n^2),但是可以通过使用堆优化来将其优化到O(nlogn)。 ### 回答2: Dijkstra算法是一种解决单源最短路径问题的贪心算法,其思想是利用“松弛”操作来不断更新当前点到源点的最短距离,但前提是所有边的权重非负。如果有负权边,则需要使用Bellman-Ford算法。 首先,我们需要定义一个数组dis数组,用于存储源点s到各个点的最短距离。dis[s]初始为0,其他点初始为无限大。接着,我们需要维护一个合S,表示已经求出最短路径的点的合。将源点s加入合S中。 对于每个未加入S的点v,我们通过选择其它点到源点s的最短路径中的一个点u,然后将dis[v]更新为dis[u] + w(u,v),其中w(u,v)表示边(u,v)的权重。具体地,这个操作称为“松弛”操作。 在松弛操作中,我们需要比较dis[u] + w(u,v)和dis[v]的大小,如果前者更小,则更新dis[v]的值为dis[u] + w(u,v)。 重复执行以上操作,直到所有的点都加入到合S中。最后dis数组中存储的就是源点s到所有点的最短距离。 Dijkstra算法可以用堆优化,时间复杂度为O(mlogn),其中n表示图中的点数,m表示边数。Dijkstra算法也可以应用于稠密图,时间复杂度为O(n^2)。 总之,Dijkstra算法是一种经典的求解单源最短路径问题的算法,其实现简单,效率高,被广泛应用于路由算法和图像处理等领域。 ### 回答3: Dijkstra算法是一种在加权有向图中寻找从源节点到其他节点的最短路径的贪心算法。该算法基于其它路径加权节点的已知最短路径去更新更长路径的信息直到找到从源节点到目标节点的最短路径。在整个计算过程中,Dijkstra算法需要维护一个待处理节点合和一个距离源节点的最短路径数组。 算法的具体实现如下: 1. 初始化源节点及其距离为0,其他节点的距离为无穷大。 2. 将源节点加入到待处理节点合中。 3. 对于源节点的所有相邻节点,更新它们距离源节点的最短路径。如果当前路径小于之前已知的最短路径,则更新最短路径数组。 4. 遍历待处理节点合中除源节点外的节点,选择距离最近的节点作为当前节点,并将它从待处理机合中移除。 5. 对于当前节点的所有相邻节点,更新它们距离源节点的最短路径。如果当前路径小于之前已知的最短路径,则更新最短路径数组。 6. 重复步骤4和5,直到待处理节点合为空或者目标节点已经被遍历。 Dijkstra算法的时间复杂度为O(n^2),其中n为节点数,由于它是贪心算法,只能处理非负权重的图,否则可能会陷入死循环。但是,Dijkstra算法是单源最短路径问题的最优解,因此在处理小规模的图时效果很好。在处理大规模图时,需要使用其他高效的算法,如A*算法、Bellman-Ford算法等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值