小明的火车旅行计划


题目

by Zhenqian Shen

描述

给定有向图, 其中有 \(N\) 个节点和 \(M\) 条边, 每条边有时间和路费. 请求在时间限制下从起点到终点的最小路费; 若不存在, 返回 -1.

提示: 节点数小于 \(2^{16}\), 边数小于 \(2^{20}\), 时间和路费均小于 \(2^{16}\).

IO格式

输入:

N M
第1条边发端 第1条边末端 第1条边时间 第1条边路费
... // 起点按非递减排列
起点 终点 时间限制

输出:

最小花费或-1

样例

输入:

4 5
0 1 1 1
0 2 4 2
1 2 1 5
1 3 4 2
2 3 1 1
0 3 4

输出:

7

解答

思路

这同样是一道比较令人难受的题目, 我没有找到能够过全部 10 个点单一算法.

缺失数据恢复一样, 这题同样利用判题点有限的特点, 选用两个算法合并求解.

具体而言, 我先后采用了 DFS 及贪心的 DFS. DFS 的剪枝条件是时间超过限制和路费超过现得解; 贪心是通过对邻接表排序, 每次取最小的邻边, 这样当得到第一个解时, 理论上也是全局解.

代码

图的存储

这里采取的是索引表+一维数组实现的邻接表. 因为输入边的起点是非递减的, 因此这样的结构有利于处理.

struct Net {
    struct Path {
        Path() : target(0), time(0), cost(0) {}
        ui target;
        ui time;
        ui cost;
    };
    struct Node {
        Node() : pos(nullptr), len(0) {}
        Path* pos;
        ui len;
    };
    Net(ui n, ui m) : N(n), M(m), nodes(new Node[n]), paths(new Path[m]) {}
    ~Net() {
        delete[] nodes;
        delete[] paths;
    }
    ui N, M;
    Node* nodes;
    Path* paths;
};

剪枝 DFS

struct Result {
    ui ret;
    bool res;
};

Result dfs(Net& net) {
    struct Status {
        ui curTime;
        ui curCost;
    };
    Result result{0, false};
    stack<Net::Path*> s;
    net.adj(s, origin);
    stack<Status> st;
    st.push(Status{0, 0});
    bool* vis = new bool[net.N]{false};
    vis[origin] = true;
    while (!s.empty()) {
        auto* cur = s.top();
        if (vis[cur->target]) {
            vis[cur->target] = false;
            s.pop();
            st.pop();
        } else {
            Status prevSt = st.top(),
                   curSt = Status{prevSt.curTime + cur->time};
            if (curSt.curTime > timeLmt) {
                s.pop();
                continue;
            }
            curSt.curCost = prevSt.curCost + cur->cost;
            if (result.res) {
                if (curSt.curCost >= result.ret)
                    s.pop();
                else {
                    if (cur->target == dest) {
                        result.ret = curSt.curCost;
                        s.pop();
                    } else {
                        vis[cur->target] = true;
                        st.push(curSt);
                        net.adj(s, cur->target, vis);
                    }
                }
            } else {
                if (cur->target == dest) {
                    result.ret = curSt.curCost;
                    result.res = true;
                    s.pop();
                } else {
                    vis[cur->target] = true;
                    st.push(curSt);
                    net.adj(s, cur->target, vis);
                }
            }
        }
    }
    return result;
}

递归版本:

bool* vis;
ui result = UINT_MAX;

void dfs(Net& net, Net::Path* cur, ui curTime, ui curCost) {
    ui tmpTime = curTime + cur->time;
    if (tmpTime > timeLmt) return;
    ui tmpCost = curCost + cur->cost;
    if (tmpCost >= result) return;
    if (cur->target == dest)
        result = tmpCost;
    else {
        auto* begin = net.nodes[cur->target].pos;
        auto* end = begin + net.nodes[cur->target].len;
        for (auto* i = begin; i < end; i++) {
            if (!vis[i->target]) {
                vis[i->target] = true;
                dfs(net, i, tmpTime, tmpCost);
                vis[i->target] = false;
            }
        }
    }
}

void dfs(Net& net) {
    vis = new bool[net.N];
    vis[origin] = true;
    auto* begin = net.nodes[origin].pos;
    auto* end = begin + net.nodes[origin].len;
    for (auto* i = begin; i < end; i++) {
        if (!vis[i->target]) {
            vis[i->target] = true;
            dfs(net, i, 0, 0);
            vis[i->target] = false;
        }
    }
}

贪心 DFS

struct Result {
    ui ret;
    bool res;
};

Result dfs(Net& net) {
    struct Status {
        ui curTime;
        ui curCost;
    };
    Result result{0, false};
    stack<Net::Path*> s;
    net.adj(s, origin);
    stack<Status> st;
    st.push(Status{0, 0});
    bool* vis = new bool[net.N]{false};
    vis[origin] = true;
    while (!s.empty()) {
        auto* cur = s.top();
        if (vis[cur->target]) {
            vis[cur->target] = false;
            s.pop();
            st.pop();
        } else {
            Status prevSt = st.top(),
                   curSt = Status{prevSt.curTime + cur->time};
            if (curSt.curTime > timeLmt) {
                s.pop();
                continue;
            }
            curSt.curCost = prevSt.curCost + cur->cost;
            if (cur->target == dest) {
                result.ret = curSt.curCost;
                result.res = true;
                break;
            } else {
                vis[cur->target] = true;
                st.push(curSt);
                net.adj(s, cur->target, vis);
            }
        }
    }
    delete[] vis;
    return result;
}

最终提交

#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;

#define prf printf
#define scf scanf
using ui = unsigned int;

struct Net {
    struct Path {
        Path() : target(0), time(0), cost(0) {}
        ui target;
        ui time;
        ui cost;
    };
    struct Node {
        Node() : pos(nullptr), len(0) {}
        Path* pos;
        ui len;
    };

    Net(ui n, ui m) : N(n), M(m), nodes(new Node[n]), paths(new Path[m]) {}
    ~Net() {
        delete[] nodes;
        delete[] paths;
    }
    ui N, M;
    Node* nodes;
    Path* paths;

    void in() {
        ui cur = 0;
        ui temp;
        nodes[0].pos = paths;
        for (ui i = 0u; i < M; i++) {
            scf("%u%u%u%u", &temp, &paths[i].target, &paths[i].time,
                &paths[i].cost);
            if (temp == cur)
                nodes[cur].len++;
            else {
                cur = temp;
                nodes[cur].pos = &paths[i];
                nodes[cur].len = 1;
            }
        }
        for (ui i = 0u; i < N; i++) {
            auto* begin = nodes[i].pos;
            auto* end = begin + nodes[i].len;
            sort(begin, end,
                 [](Path& a, Path& b) -> bool { return a.cost > b.cost; });
        }
    }
    void adj(stack<Path*>& s, ui node) {
        auto* begin = nodes[node].pos;
        auto* end = begin + nodes[node].len;
        for (auto* i = begin; i < end; i++) s.push(i);
    }
    void adj(stack<Path*>& s, ui node, bool* vis) {
        auto* begin = nodes[node].pos;
        auto* end = begin + nodes[node].len;
        for (auto* i = begin; i < end; i++)
            if (!vis[i->target]) s.push(i);
    }
};

ui origin, dest, timeLmt;

struct Result {
    ui ret;
    bool res;
};

Result dfs1(Net& net) {
    struct Status {
        ui curTime;
        ui curCost;
    };
    Result result{0, false};
    stack<Net::Path*> s;
    net.adj(s, origin);
    stack<Status> st;
    st.push(Status{0, 0});
    bool* vis = new bool[net.N]{false};
    vis[origin] = true;
    while (!s.empty()) {
        auto* cur = s.top();
        if (vis[cur->target]) {
            vis[cur->target] = false;
            s.pop();
            st.pop();
        } else {
            Status prevSt = st.top(),
                   curSt = Status{prevSt.curTime + cur->time};
            if (curSt.curTime > timeLmt) {
                s.pop();
                continue;
            }
            curSt.curCost = prevSt.curCost + cur->cost;
            if (result.res) {
                if (curSt.curCost >= result.ret)
                    s.pop();
                else {
                    if (cur->target == dest) {
                        result.ret = curSt.curCost;
                        s.pop();
                    } else {
                        vis[cur->target] = true;
                        st.push(curSt);
                        net.adj(s, cur->target, vis);
                    }
                }
            } else {
                if (cur->target == dest) {
                    result.ret = curSt.curCost;
                    result.res = true;
                    s.pop();
                } else {
                    vis[cur->target] = true;
                    st.push(curSt);
                    net.adj(s, cur->target, vis);
                }
            }
        }
    }
    delete[] vis;
    return result;
}

Result dfs2(Net& net) {
    struct Status {
        ui curTime;
        ui curCost;
    };
    Result result{0, false};
    stack<Net::Path*> s;
    net.adj(s, origin);
    stack<Status> st;
    st.push(Status{0, 0});
    bool* vis = new bool[net.N]{false};
    vis[origin] = true;
    while (!s.empty()) {
        auto* cur = s.top();
        if (vis[cur->target]) {
            vis[cur->target] = false;
            s.pop();
            st.pop();
        } else {
            Status prevSt = st.top(),
                   curSt = Status{prevSt.curTime + cur->time};
            if (curSt.curTime > timeLmt) {
                s.pop();
                continue;
            }
            curSt.curCost = prevSt.curCost + cur->cost;
            if (result.res) {
                if (curSt.curCost >= result.ret)
                    s.pop();
                else {
                    if (cur->target == dest) {
                        result.ret = curSt.curCost;
                        s.pop();
                    } else {
                        vis[cur->target] = true;
                        st.push(curSt);
                        net.adj(s, cur->target, vis);
                    }
                }
            } else {
                if (cur->target == dest) {
                    result.ret = curSt.curCost;
                    result.res = true;
                    break;
                } else {
                    vis[cur->target] = true;
                    st.push(curSt);
                    net.adj(s, cur->target, vis);
                }
            }
        }
    }
    delete[] vis;
    return result;
}

int main() {
    ui N, M;
    scf("%u%u", &N, &M);
    Net net(N, M);
    net.in();
    scf("%u%u%u", &origin, &dest, &timeLmt);
    auto result = M > 100000 ? dfs2(net) : dfs1(net);
    if (result.res)
        prf("%u", result.ret);
    else
        prf("-1");
    return 0;
}

时空消耗

1	Accepted	0 ms	912 KB
2	Accepted	0 ms	912 KB
3	Accepted	0 ms	924 KB
4	Accepted	4 ms	960 KB
5	Accepted	0 ms	940 KB
6	Accepted	4 ms	1080 KB
7	Accepted	16 ms	1324 KB
8	Accepted	16 ms	1392 KB
9	Accepted	12 ms	1584 KB
10	Accepted	840 ms	3316 KB