ACM模版

描述

题解

说实在的,这道题我没有完全解决,所以写这个博客不是为了帮助别人解决这个问题,而是希望有朋友帮我找到我的 bug,我找了大半天也没有找到问题所在,30分只得了19分,尴尬症犯了。

为了方便大神们看我的代码,我先写一下自己的思路,这里我用的 Dijkstra 的队列+邻接表(堆)优化解法,多标签搜索,除了基础的距离 dist[]、vis[],我又开了三个标签,pre[]:存储父节点,记录路径,pre_[]:父节点所在路线(转接点算到上一条路线),cnt[]:累计经过路线数目。

另外需要说明的是,题目规定,每一个运营区间只会承包给一家公司,所以我又在邻接表中添加了一个公司(线路)标签,记录每一个区间所承包的公司(线路)。其他过程和普通的 dijkstra 算法没有什么区别,但是就是莫名其妙的挂了11组数据,请求大神们悉心查阅,如果发现了我的 bug 并知会与我,我将十分感谢,万分膜拜~~~

问题代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#include <set>

using namespace std;

/* * 使用优先队列优化Dijkstra算法 * 复杂度O(ElongE) * 注意对vector<Edge> E[MAXN]进行初始化后加边 */

const int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;

struct qNode
{
    int v;
    int c;
    qNode(int _v = 0, int _c = 0) : v(_v), c(_c) {}
    bool operator < (const qNode &r) const
    {
        return c > r.c;
    }
};

struct Edge
{
    int v;
    int cost;
    Edge(int _v = 0, int _cost = 0) : v(_v), cost(_cost) {}
};

vector<pair<Edge, int> > E[MAXN];
bool vis[MAXN];
int dist[MAXN];     // 最短路距离
int pre[MAXN];      // 父节点,记录路径
int pre_[MAXN];     // 父节点所在路线(转接点算到上一条路线)
int cnt[MAXN];      // 累计经过路线数目

void Dijkstra(int n, int start)     // 点的编号从1开始
{
    memset(vis, false, sizeof(vis));
    memset(dist, 0x3f, sizeof(dist));
    memset(pre, -1, sizeof(pre));
    memset(pre_, -1, sizeof(pre_));
    memset(cnt, -1, sizeof(cnt));
    priority_queue<qNode> que;

    while (!que.empty())
    {
        que.pop();
    }
    dist[start] = 0;
    que.push(qNode(start, 0));
    qNode tmp;

    while (!que.empty())
    {
        tmp = que.top();
        que.pop();
        int u = tmp.v;
        if (vis[u])
        {
            continue;
        }
        vis[u] = true;
        for (int i = 0; i < E[u].size(); i++)
        {
            int v = E[u][i].first.v;
            int cost = E[u][i].first.cost;
            if (!vis[v] && dist[v] > dist[u] + cost)
            {
                dist[v] = dist[u] + cost;
                pre[v] = u;
                if (pre_[u] == -1 || E[u][i].second != pre_[u])
                {
                    if (pre_[u] == -1)
                    {
                        pre_[u] = E[u][i].second;
                        cnt[u] = 1;
                    }
                    pre_[v] = E[u][i].second;
                    cnt[v] = pre_[v] == pre_[u] ? cnt[u] : cnt[u] + 1;
                }
                else
                {
                    pre_[v] = pre_[u];
                    cnt[v] = cnt[u];
                }
                que.push(qNode(v, dist[v]));
            }
            else if (!vis[v] && dist[v] == dist[u] + cost)
            {
                if (E[u][i].second == pre_[u])
                {
                    if (cnt[pre[v]] > cnt[u])
                    {
                        pre[v] = u;
                        pre_[v] = pre_[u];
                        cnt[v] = cnt[u];
                        que.push(qNode(v, dist[v]));
                    }
                }
                else
                {
                    if (cnt[pre[v]] > cnt[u] + 1)
                    {
                        pre[v] = u;
                        pre_[v] = E[u][i].second;
                        cnt[v] = cnt[u] + 1;
                        que.push(qNode(v, dist[v]));
                    }
                }
            }
        }
    }
}

void addEdge(int u, int v, int w, int c)
{
    E[u].push_back(make_pair(Edge(v, w), c));
}

struct way
{
    int id;
    int cmp;
} w[MAXN];

void getWay(int ed, int mt)
{
    int m = mt;
    w[m].id = ed;
    w[m--].cmp = pre_[ed];
    while (pre[ed] != -1)
    {
        ed = pre[ed];
        w[m].id = ed;
        w[m--].cmp = pre_[ed];
    }
    w[1].id = ed;
    w[1].cmp = pre_[ed];

    int sc = w[1].cmp;
    int si = w[1].id;
    for (int i = 2; i <= mt; i++)
    {
        if (w[i].cmp != sc)
        {
            printf("Go by the line of company #%d from %04d to %04d.\n", sc, si, w[i - 1].id);
            sc = w[i].cmp;
            si = w[i - 1].id;
        }
    }
    printf("Go by the line of company #%d from %04d to %04d.\n", sc, si, w[mt].id);
}

int main()
{
// freopen("/Users/zyj/Desktop/input.txt", "r", stdin);

    int N;
    cin >> N;

    int M, u, v;
    for (int i = 1; i <= N; i++)
    {
        scanf("%d%d", &M, &u);
        for (int j = 1; j < M; j++)
        {
            scanf("%d", &v);
            addEdge(u, v, 1, i);
            addEdge(v, u, 1, i);
            u = v;
        }
    }

    int K;
    cin >> K;
    for (int i = 0; i < K; i++)
    {
        scanf("%d%d", &u, &v);
        Dijkstra(N, u);
        if (dist[v] == INF)
        {
            cout << "Sorry, no line is available.\n";
        }
        else
        {
            cout << dist[v] << '\n';
            getWay(v, dist[v] + 1);
        }
    }

    return 0;
}

2017.3.31 1:36 更新

根据评论区大牛 欣君 提供的一组样例,我进行了一些修正,将getWay()函数做了微调整,从原来的 19 分变成了 25 分,除了第三组外其他两组原来没有通过的样例通过了,但是,我依然没有成功 AC,哎,伤脑筋啊,还有啥 bug 啊~~~哭晕在键盘上:-)

修改 One

void getWay(int ed, int mt);

void getWay(int ed, int mt)
{
    int m = mt;
    w[m].id = ed;
    w[m--].cmp = pre_[ed];
    while (pre[ed] != -1)
    {
        ed = pre[ed];
        w[m].id = ed;
        w[m--].cmp = pre_[ed];
    }
    w[1].id = ed;
    w[1].cmp = pre_[ed];
    if (w[1].cmp != w[2].cmp)   //  此判断为最新修改
    {
        w[1].cmp = w[2].cmp;
    }

    int sc = w[1].cmp;
    int si = w[1].id;
    for (int i = 2; i <= mt; i++)
    {
        if (w[i].cmp != sc)
        {
            printf("Go by the line of company #%d from %04d to %04d.\n", sc, si, w[i - 1].id);
            sc = w[i].cmp;
            si = w[i - 1].id;
        }
    }
    printf("Go by the line of company #%d from %04d to %04d.\n", sc, si, w[mt].id);
}

这样改,可以成功避免起点自成一路的情况,毕竟自成一路也就意味着没有经过该路线的任一区间,这样不合法。

另外,发现题目有漏洞,让输出经停站数量(始发地和目的地不包括在内),可是最后结果却比真实结果大一,也就是说,始发地和目的地有一个包含在内了,例如样例中:3011 - 3013 的具体路线为 3011 - 3812 - 3013,也就是说,除去始发地和目的地外,经停站应该为1才对,而样例结果却为2,所以这是题目的一个漏洞。


2017.4.1 0:55 更新

十分感谢评论区 欣君 大神,又给我提供了一组错误样例,助我成功找到 bug。

对于大神所言,我没有考虑在站数最少的情况下线路数要最少的规定,实际上不然,我考虑了,然而 bug 导致我的线路修正出了问题,由于我想到了这个问题,所以找 bug 时我以为我考虑过了,所以就没有仔细斟酌这个过程,然而大神给我提供的样例告诉我,我的处理没有成功,究其原因,是搞错了一个 if 判断并且少了一个起点的特殊处理,修改位置均在 Dijkstra() 函数中。

修改 Two

void Dijkstra(int n, int start);

void Dijkstra(int n, int start)     // 点的编号从1开始
{
    memset(vis, false, sizeof(vis));
    memset(dist, 0x3f, sizeof(dist));
    memset(pre, -1, sizeof(pre));
    memset(pre_, -1, sizeof(pre_));
    memset(cnt, -1, sizeof(cnt));
    priority_queue<qNode> que;

    while (!que.empty())
    {
        que.pop();
    }
    dist[start] = 0;
    que.push(qNode(start, 0));
    qNode tmp;

    while (!que.empty())
    {
        tmp = que.top();
        que.pop();
        int u = tmp.v;
        if (vis[u])
        {
            continue;
        }
        vis[u] = true;
        for (int i = 0; i < E[u].size(); i++)
        {
            int v = E[u][i].first.v;
            int cost = E[u][i].first.cost;
            if (!vis[v] && dist[v] > dist[u] + cost)
            {
                dist[v] = dist[u] + cost;
                pre[v] = u;
                if (pre_[u] == -1 || E[u][i].second != pre_[u] || u == start)   // 4.1
                {
                    if (pre_[u] == -1 || u == start)    // 4.1
                    {
                        pre_[u] = E[u][i].second;
                        cnt[u] = 1;
                    }
                    pre_[v] = E[u][i].second;
                    cnt[v] = pre_[v] == pre_[u] ? cnt[u] : cnt[u] + 1;
                }
                else
                {
                    pre_[v] = pre_[u];
                    cnt[v] = cnt[u];
                }
                que.push(qNode(v, dist[v]));
            }
            else if (!vis[v] && dist[v] == dist[u] + cost)
            {
                if (E[u][i].second == pre_[u])
                {
                    if (cnt[v] > cnt[u])        // 4.1
                    {
                        pre[v] = u;
                        pre_[v] = pre_[u];
                        cnt[v] = cnt[u];
                        que.push(qNode(v, dist[v]));
                    }
                }
                else
                {
                    if (cnt[v] > cnt[u] + 1)    // 4.1
                    {
                        pre[v] = u;
                        pre_[v] = E[u][i].second;
                        cnt[v] = cnt[u] + 1;
                        que.push(qNode(v, dist[v]));
                    }
                }
            }
        }
    }
}

终于拿到30分了,十分开心,为了方便大家查看代码,完整的正确代码再贴一遍喽~~~

AC 代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

/* * 使用优先队列优化Dijkstra算法 * 复杂度O(ElongE) * 注意对vector<Edge> E[MAXN]进行初始化后加边 */

const int INF = 0x3f3f3f3f;
const int MAXN = 1e5 + 10;

struct qNode
{
    int v;
    int c;
    qNode(int _v = 0, int _c = 0) : v(_v), c(_c) {}
    bool operator < (const qNode &r) const
    {
        return c > r.c;
    }
};

struct Edge
{
    int v;
    int cost;
    Edge(int _v = 0, int _cost = 0) : v(_v), cost(_cost) {}
};

vector<pair<Edge, int> > E[MAXN];
bool vis[MAXN];
int dist[MAXN];     // 最短路距离
int pre[MAXN];      // 父节点,记录路径
int pre_[MAXN];     // 父节点所在路线(转接点算到上一条路线)
int cnt[MAXN];      // 累计经过路线数目

void Dijkstra(int n, int start)     // 点的编号从1开始
{
    memset(vis, false, sizeof(vis));
    memset(dist, 0x3f, sizeof(dist));
    memset(pre, -1, sizeof(pre));
    memset(pre_, -1, sizeof(pre_));
    memset(cnt, -1, sizeof(cnt));
    priority_queue<qNode> que;

    while (!que.empty())
    {
        que.pop();
    }
    dist[start] = 0;
    que.push(qNode(start, 0));
    qNode tmp;

    while (!que.empty())
    {
        tmp = que.top();
        que.pop();
        int u = tmp.v;
        if (vis[u])
        {
            continue;
        }
        vis[u] = true;
        for (int i = 0; i < E[u].size(); i++)
        {
            int v = E[u][i].first.v;
            int cost = E[u][i].first.cost;
            if (!vis[v] && dist[v] > dist[u] + cost)
            {
                dist[v] = dist[u] + cost;
                pre[v] = u;
                if (pre_[u] == -1 || E[u][i].second != pre_[u] || u == start)   // 4.1
                {
                    if (pre_[u] == -1 || u == start)    // 4.1
                    {
                        pre_[u] = E[u][i].second;
                        cnt[u] = 1;
                    }
                    pre_[v] = E[u][i].second;
                    cnt[v] = pre_[v] == pre_[u] ? cnt[u] : cnt[u] + 1;
                }
                else
                {
                    pre_[v] = pre_[u];
                    cnt[v] = cnt[u];
                }
                que.push(qNode(v, dist[v]));
            }
            else if (!vis[v] && dist[v] == dist[u] + cost)
            {
                if (E[u][i].second == pre_[u])
                {
                    if (cnt[v] > cnt[u])        // 4.1
                    {
                        pre[v] = u;
                        pre_[v] = pre_[u];
                        cnt[v] = cnt[u];
                        que.push(qNode(v, dist[v]));
                    }
                }
                else
                {
                    if (cnt[v] > cnt[u] + 1)    // 4.1
                    {
                        pre[v] = u;
                        pre_[v] = E[u][i].second;
                        cnt[v] = cnt[u] + 1;
                        que.push(qNode(v, dist[v]));
                    }
                }
            }
        }
    }
}

void addEdge(int u, int v, int w, int c)
{
    E[u].push_back(make_pair(Edge(v, w), c));
}

struct way
{
    int id;
    int cmp;
} w[MAXN];

void getWay(int ed, int mt)
{
    int m = mt;
    w[m].id = ed;
    w[m--].cmp = pre_[ed];
    while (pre[ed] != -1)
    {
        ed = pre[ed];
        w[m].id = ed;
        w[m--].cmp = pre_[ed];
    }
    w[1].id = ed;
    w[1].cmp = pre_[ed];
    if (w[1].cmp != w[2].cmp)   // 3.31
    {
        w[1].cmp = w[2].cmp;
    }

    int sc = w[1].cmp;
    int si = w[1].id;
    for (int i = 2; i <= mt; i++)
    {
        if (w[i].cmp != sc)
        {
            printf("Go by the line of company #%d from %04d to %04d.\n", sc, si, w[i - 1].id);
            sc = w[i].cmp;
            si = w[i - 1].id;
        }
    }
    printf("Go by the line of company #%d from %04d to %04d.\n", sc, si, w[mt].id);
}

int main()
{
// freopen("/Users/zyj/Desktop/input.txt", "r", stdin);

    int N;
    cin >> N;

    int M, u, v;
    for (int i = 1; i <= N; i++)
    {
        scanf("%d%d", &M, &u);
        for (int j = 1; j < M; j++)
        {
            scanf("%d", &v);
            addEdge(u, v, 1, i);
            addEdge(v, u, 1, i);
            u = v;
        }
    }

    int K;
    cin >> K;
    for (int i = 0; i < K; i++)
    {
        scanf("%d%d", &u, &v);
        Dijkstra(N, u);
        if (dist[v] == INF)
        {
            cout << "Sorry, no line is available.\n";
        }
        else
        {
            cout << dist[v] << '\n';
            getWay(v, dist[v] + 1);
        }
    }

    return 0;
}