【PAT甲级 单源最短路径 C++】1018 Public Bike Management (30 分)

这种点权或边权不是简单相加的题,一般来说就必须使用DFS来计算

Dijkstra + DFS

# include <iostream>
# include <vector>
# include <algorithm>

using namespace std;

/* 求最短路径,第二标尺是从起点带最少的单车 */ 

const int MAXV = 510;
const int INF  = 0xffffff; 

struct Node{
    int v, dis;
};

vector<int> V(510);  // 点权
vector<Node> G[510]; // 边权
int dist[510];
int vis[510];
vector<int> pre[510];

int Cmax, N, ed, M;
int st = 0; // 起点是索引0; 


void Dijkstra(int s){
    fill(dist, end(dist), INF);
    fill(vis, end(vis), false);
    dist[s] = 0;
    
    for(int i = 0;i < N;++i){
        int u = -1;
        int MIN = INF;
        for(int j = 0; j < N ;++j){
            if(vis[j] == false && dist[j] < MIN){
                u = j;
                MIN = dist[j];
            }
        }
        if(u == -1) return;
        vis[u] = true;
        for(Node next: G[u]){
            int v = next.v ,uvDis = next.dis;
            if(vis[v] == false){
                if(dist[u] + uvDis  < dist[v]){
                    dist[v] = dist[u] + uvDis;
                    pre[v].clear();
                    pre[v].push_back(u);
                }
                else
                if(dist[u] + uvDis == dist[v]){
                    pre[v].push_back(u);
                }
            }
            
        }
    }
}

int minNeed = INF, minRemain = INF; // 从PBMC出发带走的单车数量,从终点带回PBMC的单车数量
vector<int> path, tempPath;

void DFS(int v){
    if(v == st){
        tempPath.push_back(st);

        int need = 0, remain = 0;
        for(int i = tempPath.size() - 1;i >= 0;--i){
            int id = tempPath[i];
            if(V[id] > 0){ // 当前站点补给多了,不需要补给,需要带走补给,所以remain的多了 ,need不变
                remain += V[id]; // 补给数量remain变多了
                // need = need; // 不需要补给,所以need不变
            } 
            else{ // 当前站点需要补给,所以remain少了,need多了
                if( remain < (-V[id]) ){ // 带来的不够补给
                    need += (-V[id]) - remain; // 所以need还需要(-V[id]) - remain数量的更多补给
                    remain = 0; // 把能补给的都补给完了
                } else { // 带来的够补给
                    remain -= (-V[id]); // 所以需要补给(-V[id])数量
                    // need = need; // 够补给,所以need不变
                }
            }
        }
        if(need < minNeed){
            minNeed = need;
            minRemain = remain;
            path = tempPath;
        } 
        else
        if(need == minNeed && remain < minRemain){
            minRemain = remain;
            path = tempPath;
        }
        tempPath.pop_back();
    }

    tempPath.push_back(v);
    for(int u: pre[v]){
        DFS(u);
    }
    tempPath.pop_back();
}

int main(){
    cin >> Cmax >> N >> ed >> M;
    for(int i = 1;i <= N;++i){
        cin >> V[i];
        V[i] -= Cmax/2;
    }V[0] = 0;
    for(int i = 0;i < M;++i){
        int u, v, len;
        cin >> u >> v >> len;
        G[u].push_back({v, len});
        G[v].push_back({u, len});
    }
    Dijkstra(st);
    DFS(ed);
    
    cout << minNeed << " ";
    for(int i = path.size() - 1;i >= 0;--i){
        cout << path[i];
        if(i != 0) 
            cout << "->";
        else
            cout << " ";
    }
    cout << minRemain << endl;

    
    return 0;
}
支限界法(Branch and Bound)是一种用于求解组合优化问题的搜索算法,特别是在求解最短路径问题上,常用于解决像旅行商问题(Traveling Salesman Problem, TSP)这类NP完全问题。在处理单源最短路径问题时,虽然Dijkstra算法和Floyd-Warshall算法更常见且效率更高,但支限界法在处理大规模图或者有额外约束的情况时会很有用。 在C++中实现支限界法解决单源最短路径问题,比如带有特定限制的TSP,可能会使用类似于A*搜索(A* Search Algorithm)的启发式搜索策略。以下是一个简化的步骤: 1. **定义问题状态**:通常表示为图中的节点集合和当前路径的成本(如距离或时间)。 2. **状态空间搜索**: - 创建一个优先队列来存储待探索的状态,初始化时只包含起点。 - 当队列不为空时,取出状态进行扩展(生成子节点)。 - 对每个子节点计算其成本,并应用约束条件(如时间窗口、访问顺序等)。 - 如果满足终止条件(找到目标路径或证明无法达到目标),结束搜索;否则,将满足条件的子节点加入优先队列。 3. **剪枝策略**: - 使用上界(Upper Bound)和下界(Lower Bound)进行剪枝,如果某个路径的下界大于已知的最优解,可以直接忽略。 - 可能还会使用启发式函数(Heuristic Function)来评估潜在路径的可能性,减少不必要的搜索。 4. **路径回溯**:当找到最优解时,需要回溯到搜索树中的起始节点,生成完整的路径。 相关问题: 1. 支限界法如何在最短路径问题中避免无效搜索? 2. A*搜索中的启发式函数是什么?如何选择一个合适的启发式函数? 3. 如何在C++中实现优先队列结构来支持支限界搜索? 请告诉我你是否对支限界法在最短路径中的具体实现细节感兴趣,或者想了解其他相关问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值