1 条题解

  • 0
    @ 2025-4-8 23:05:04

    题意

    NN个城市,编号11NN。城市间有RR条单向道路。 每条道路连接两个城市,有长度和过路费两个属性。 BobBob只有KK块钱,他想从城市11走到城市NN。问最短共需要走多长的路。如果到不了NN ,输出1-1

    2N1002\leq N\leq100

    0K100000\leq K\leq 10000

    1R100001\leq R\leq 10000

    每条路的长度 LL, 1L1001 \leq L \leq 100

    每条路的过路费TT , 0T1000 \leq T\leq 100

    解题思路

    从城市11开始深度优先遍历整个图,找到所有能到达NN的走法,选一个最优的。

    最优性剪枝

    如果当前已经找到的最优路径长度为LL,那么在继续搜索的过程中,总长度已经大于等于LL的走法,就可以直接放弃,不用走到底了。 另一种通用的最优性剪枝思想——保存中间计算结果用于最优性剪枝: 如果到达某个状态AA时,发现前面曾经也到达过AA,且前面那次到达AA所花代价更少,则剪枝。这要求保存到达状态AA的到目前为止的最少代价。 用midL[k][m]midL[k][m]表示:走到城市kk时总过路费为mm的条件下,最优路径的长度。若在后续的搜索中,再次走到kk时,如果总路费恰好为mm,且此时的路径长度已经超过midL[k][m]midL[k][m],则不必再走下去了。

    标程

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    using namespace std;
    
    int K, N, R; // 钱,城市,道路
    
    struct Road {
        int d, L, t; // 终点、长度、过路费
    };
    
    vector<vector<Road> > G(110); // 邻接表存储图
    int minL[110][10010]; // minL[i][j]表示从1到i点,花费j的最短路径长度
    int minLen; // 全局最短路径
    int totalLen; // 当前路径长度
    int totalCost; // 当前花费
    int visited[110]; // 访问标记
    
    void dfs(int s) {
        if (s == N) { // 到达终点
            if (totalLen < minLen) {
                minLen = totalLen;
            }
            return;
        }
        for (int i = 0; i < G[s].size(); ++i) {
            Road r = G[s][i];
            if (totalCost + r.t > K) continue; // 可行性剪枝
            if (visited[r.d]) continue; // 避免环路
            
            // 最优性剪枝
            if (totalLen + r.L >= minLen) continue;
            if (totalLen + r.L >= minL[r.d][totalCost + r.t]) continue;
            
            // 更新状态
            minL[r.d][totalCost + r.t] = totalLen + r.L;
            totalLen += r.L;
            totalCost += r.t;
            visited[r.d] = 1;
            
            dfs(r.d); // 递归搜索
            
            // 回溯
            visited[r.d] = 0;
            totalLen -= r.L;
            totalCost -= r.t;
        }
    }
    
    int main() {
        scanf("%d%d%d", &K, &N, &R);
        for (int i = 0; i < R; ++i) {
            int s;
            Road r;
            scanf("%d%d%d%d", &s, &r.d, &r.L, &r.t);
            if (s != r.d) { // 避免自环
                G[s].push_back(r);
            }
        }
        
        // 初始化
        memset(visited, 0, sizeof(visited));
        memset(minL, 0x3f, sizeof(minL));
        totalLen = 0;
        minLen = 0x3f3f3f3f; // 用大数代替1<<30
        totalCost = 0;
        visited[1] = 1;
        
        dfs(1);
        
        if (minLen != 0x3f3f3f3f) {
            printf("%d\n", minLen);
        } else {
            printf("-1\n");
        }
        
        return 0;
    }
    
    • 1

    信息

    ID
    725
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    6
    已通过
    1
    上传者