1018 Public Bike Management (30 分)

题目
在这里插入图片描述
在这里插入图片描述

题目大意:
每一个自行车站有一个最大容量(偶数),当车站中车的数量为最大容量的一半时,称该状态为完美状态。若数量小于最大容量的一半,会从控制中心携带车辆来补全;若数量大于最大容量的一半,则会从该车站带走车辆。现在给出最大容量maxNum,车站数量stationNum,问题车站的编号problemIndex,边的数量以及相应的距离。要求从控制中心到问题车站的最短路径,若有多条最短路径,选择带出去最少车辆的那条,若仍有多条,选择带回最少车辆的那条。

问题分析:
本题与普通的最短路径不同,不满足最优子结构。即不能由子问题的最优解推出问题的最优解(不能根据路径上的总车数来判定minNeed和minBack)。需要通过迪杰斯特拉算法找到所有的最短路径后,通过深度优先搜索确定每条路径的need和back,再与minNeed/minBack比较

代码分析:

变量定义:
设置二维数组来存储每条路径的长度,设置vector存储每个结点的前驱结点(由于可能有多条最短路径,有多个前驱结点,不能设置成一维数组)

const int inf = 99999999;
int minNeed=inf, minBack=inf;
int road[501][501], dis[501], weight[501];
bool visit[501];
vector<int> pre[501];//用于记录每条最短路径中每个结点的前驱结点
vector<int> path, temppath;//存储路径,多设置一个temppath的原因是在后面的DFS算法中是要将最新压入的结点弹出的,但是在输出时不用
int maxNum, stationNum, problemIndex, roadNum;

用迪杰斯特拉算法寻找最短路径:

for (int i = 0; i < stationNum + 1;i++){
    int u = -1, minn = inf;
    for (int j = 0; j < stationNum + 1;j++){
      if(visit[j]==false&&dis[j]<minn){
        u = j;
        minn = dis[j];
      }
    }
    if(u==-1)
      break;
    visit[u] = true;
    for (int j = 0; j < stationNum + 1;j++){
      if(visit[j]==false&&road[u][j]!=inf){
        if(dis[u]+road[u][j]<dis[j]){
          dis[j] = dis[u] + road[u][j];
          pre[j].clear();
          pre[j].push_back(u);
        }
        else if(dis[u]+road[u][j]==dis[j]){
          pre[j].push_back(u);
        }
      }
    }
  }

递归实现深度优先搜索:
这是本道题的一个难点,注意对递归出口的设置,注意每条最短路径遍历结束后,需要连续pop两个结点,以便下一条路径压入相应结点

void DFS(int v){
  temppath.push_back(v);
  if(v==0){
    int need = 0, back = 0;
    for (int i = temppath.size() - 1; i >= 0;i--){
    //此时temppath中存储着一条最短路径,最后一个元素是0,从0开始往后推,确定need和back
    int id = temppath[i];
    if (weight[id] > 0)
    {
      back += weight[id];
      }
      else if (weight[id] < 0&&back>=(-1*weight[id])){
        back += weight[id];
      }
      else if(weight[id]<0&&back<(-1*weight[id])){
        need = need - (weight[id] + back);
        back = 0;
      }
    }
    //一条最短路径遍历完毕,得到need与back
    if(need<minNeed){
      minNeed = need;
      minBack = back;
      path = temppath;
    }
    else if(need==minNeed&&back<minBack){
      minBack = back;
      path = temppath;
    }
    temppath.pop_back();//将最后压入的0结点抛出
    return ;//注意出口的设置
  }
  for (int j = 0; j < (int)(pre[v].size());j++){
    DFS(pre[v][j]);
  }//递归实现DFS
  temppath.pop_back();//一个结点的所有子结点遍历完后,回到上一个结点
}

AC代码

#include<bits/stdc++.h>
using namespace std;

const int inf = 99999999;
int minNeed=inf, minBack=inf;
int road[501][501], dis[501], weight[501];
bool visit[501];
vector<int> pre[501];//用于记录每条最短路径中每个结点的前驱结点
vector<int> path, temppath;//存储路径,多设置一个temppath的原因是在后面的DFS算法中是要将最新压入的结点弹出的,但是在输出时不用
int maxNum, stationNum, problemIndex, roadNum;
void DFS(int v){
  temppath.push_back(v);
  if(v==0){
    int need = 0, back = 0;
    for (int i = temppath.size() - 1; i >= 0;i--){
    //此时temppath中存储着一条最短路径,最后一个元素是0,从0开始往后推,确定need和back
    int id = temppath[i];
    if (weight[id] > 0)
    {
      back += weight[id];
      }
      else if (weight[id] < 0&&back>=(-1*weight[id])){
        back += weight[id];
      }
      else if(weight[id]<0&&back<(-1*weight[id])){
        need = need - (weight[id] + back);
        back = 0;
      }
    }
    //一条最短路径遍历完毕,得到need与back
    if(need<minNeed){
      minNeed = need;
      minBack = back;
      path = temppath;
    }
    else if(need==minNeed&&back<minBack){
      minBack = back;
      path = temppath;
    }
    temppath.pop_back();//将最后压入的0结点抛出
    return ;//注意出口的设置
  }
  for (int j = 0; j < (int)(pre[v].size());j++){
    DFS(pre[v][j]);
  }//递归实现DFS
  temppath.pop_back();//一个结点的所有子结点遍历完后,回到上一个结点
}//本题不满足最优子结构,无法通过单一的迪杰斯特拉算法完成,需要通过迪杰斯特拉算法找到所有的最短路径,再用深度优先搜索找到minNeed和minBack
int main(){
  fill(dis, dis + 501, inf);
  fill(road[0], road[0] + 501 * 501, inf);//将不存在路径赋上最大值,方便下面最短路径的判断
  dis[0] = 0;
  scanf("%d %d %d %d", &maxNum, &stationNum, &problemIndex, &roadNum);
  for (int i = 1; i <= stationNum;i++){
    scanf("%d", &weight[i]);
    weight[i] = weight[i] - maxNum / 2;
  }
  int a, b, c;
  for (int i = 0; i < roadNum;i++){
    scanf("%d %d %d", &a, &b, &c);
    road[a][b] = road[b][a] = c;
  }
  for (int i = 0; i < stationNum + 1;i++){
    int u = -1, minn = inf;
    for (int j = 0; j < stationNum + 1;j++){
      if(visit[j]==false&&dis[j]<minn){
        u = j;
        minn = dis[j];
      }
    }
    if(u==-1)
      break;
    visit[u] = true;
    for (int j = 0; j < stationNum + 1;j++){
      if(visit[j]==false&&road[u][j]!=inf){
        if(dis[u]+road[u][j]<dis[j]){
          dis[j] = dis[u] + road[u][j];
          pre[j].clear();
          pre[j].push_back(u);
        }
        else if(dis[u]+road[u][j]==dis[j]){
          pre[j].push_back(u);
        }
      }
    }
  }
  DFS(problemIndex);
  printf("%d ", minNeed);
  for (int i = path.size() - 1; i > 0;i--){
    printf("%d->", path[i]);
  }
  printf("%d %d", path[0], minBack);

  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值