题目大意:
城市里面有一些公共自行车站,每一个车站最大容纳Cmax辆车,如果该车站的车辆现在有Cmax/2辆车,那么说明它处于perfect状态,现在有一个站点Sp汇报有问题,需要控制中心(PBMC)就会找到一条距离它最短的路径,携带或者在路上回收多余的车辆带到Sp,使得它是perfect的状态,并且将多余车辆带回PBMC,现在要求找一条从PBMC到Sp的最短路径,如果该路径有多条,选择从PBMC携带车辆最少的,如果还有多条,选择从Sp回来的时候带回PBMC车辆最少的,题目保证有且仅有一条数据 。
算法思路:
根据题目描述是一个典型的单源最短路径问题,使用Dijstra算法求解最短路径,由于涉及到要输出具体的路径,所以采用Dijstra+DFS的方法,先使用Dijstra算法求得PBMC到Sp的距离最短的所有路径,然后再用DFS遍历所有路径选择符合第二标尺,也就是PBMC携带车辆最少的或者带回PBMC车辆最少的路径.
数据的存储:
使用G存储图,visited为访问标记数组,bikes_number数组为每个结点的点权,pre数组保存每个结点的所有前驱结点,shortest为DFS遍历中保存单条路径,result用来保存最优路径,dis数组保存起点到其他节点的最短距离,min_need_sent_bike表示最少需要起点派送的车辆数目,min_need_back_bike表示需要最少需要从终点送回的车辆数目
Dijkstra算法求解
我们循环N次,每一次循环,首先从所有未访问的点中找到距离起点最小的点nearest,然后再优化借助nearest到其他未访问的点的最短距离,如果借助nearest到达当前点j更短,那么就更新到达j的最短距离,最小耗费和到达j的前驱节点。如果最短距离一样但是通过nearest到达j的耗费更小,那么就更新到达j的最小耗费和j的前驱节点。
DFS算法过程:
在遍历所有最短路径的过程中需要重点强调的是调整结点状态的方式,一定得是从起点开始依次调整每一个节点的状态,其它的方法根本不可行,距离起点较近的节点的车辆数目较少,但是距离起点较远的车辆数目较多且恰好可以满足,这种情况下还是得从起点派送车辆调整车辆少的节点,并且将多余的车辆带回,也就是说,不能使用$n*(Cmax/2)$的方式获得最佳车辆总数,然后用现有路径的车辆总数比较相减,因为就算最后一个结点有非常多的车辆,也没有办法贡献给在路径前面的结点,题目的原话为
all the stations on the way will be adjusted as well.
也就是单向调整的。我们每次获得一条最短路径,就从起点的下一个节点开始遍历,如果当前车辆过多就需要带走,使用need_back_bike记录带回的车辆数目,如果当前站点车辆过少,就需要先使用在路上收集到的车辆进行填补,如果不够再从起点进行派送,need_sent_bike记录派送的车辆数目,如果当前路径需要派送的车辆数目更少或者派送数目相同,但是带回车辆数目更少,就更新min_need_sent_bike,min_need_back_bike和result。
注意点:
- 1、调整方向为单向调整,不能使用后面站点多余的车辆填补前面的站点。
- 2、从路上收集的车辆数目不够用的时候,得先填补当前站点车辆的空缺,再将need_back_bike置为0.
提交结果:
AC代码:
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int Cmax,N,Sp,M;// 最大容量,站点个数,问题站点编号,道路数量
int bikes_number[505];// 点权,每一个站点的车辆数目
int G[505][505];// 边权,两站点之间耗费时间
bool visited[505];// 访问标记数组
int dis[505];// 每一个站点到起点的距离
vector<int> pre[505];// 保存每一个站点的前驱结点
vector<int> shortest;//最短路径
vector<int> result;// 最优路径
int min_need_sent_bike = 0x3fffffff;//最少需要起点派送的车辆数目
int min_need_back_bike = 0x3fffffff;//最少需要从终点送回的车辆数目
// 使用深度优先搜索找到需要派送车辆最少的最短路径
void DFS(int start){
// 先访问当前节点
shortest.push_back(start);
if(pre[start].empty()){
// 当前节点是起点,没有前驱,计算该路径上需要派送的车辆
int need_sent_bike = 0;// 需要派送的车辆数目
int need_back_bike = 0;// 需要送回的车辆数目
int perfect_bike = Cmax/2;// 完美状态车辆数目
for(int i=shortest.size()-2;i>=0;--i){// 循环得排除起点
if(bikes_number[shortest[i]]>perfect_bike){
// 车辆过多需要带走
need_back_bike += (bikes_number[shortest[i]]-perfect_bike);
}else if(bikes_number[shortest[i]]<perfect_bike){
// 当前站点,车辆过少
int need = perfect_bike-bikes_number[shortest[i]];// 当前站点需要的车辆数目
if(need_back_bike>=need){
// 从路上收集的车辆数目够用
need_back_bike -= need;// 这里得是累减
}else {
// 不够用
need_sent_bike += need-need_back_bike;// 需要派送剩余需要车辆,这里得是累加
need_back_bike = 0;// 这句话得在后面
}
}
}
if(need_sent_bike<min_need_sent_bike){
// 当前路径需要派送的车辆数目更少
min_need_sent_bike = need_sent_bike;
min_need_back_bike = need_back_bike;
result = shortest;
}else if(need_sent_bike==min_need_sent_bike&&min_need_back_bike>need_back_bike){
// 派送车辆一样,需要送回的车辆更少
min_need_back_bike = need_back_bike;
result = shortest;
}
shortest.pop_back();
return;
}
for(int i=0;i<pre[start].size();++i){
DFS(pre[start][i]);
}
// 当前结点及其前驱都访问完毕,回溯
shortest.pop_back();
}
void Dijkstra(){
// 初始化
fill(dis,dis+505,0x3fffffff);
dis[0] = 0;
// 循环N+1次
for(int i=0;i<=N;++i){
// 首先从所有未访问的点中找到距离起点最小的点
int nearest,minDis=0x3fffffff;
for(int j=0;j<=N;++j){
if(!visited[j]&&minDis>dis[j]){
nearest = j;
minDis = dis[j];
}
}
if(minDis==0x3fffffff) return ;// 没有找到,说明其他点不再连通
// 优化借助nearest到其他未访问的点的距离
visited[nearest] = true;
for(int j=0;j<=N;++j){
if(!visited[j]&&G[nearest][j]!=0){
if(dis[j]>dis[nearest]+G[nearest][j]){
dis[j] = dis[nearest]+G[nearest][j];
pre[j].clear();
pre[j].push_back(nearest);
}else if(dis[j]==dis[nearest]+G[nearest][j]){
pre[j].push_back(nearest);
}
}
}
}
}
int main(){
scanf("%d %d %d %d",&Cmax,&N,&Sp,&M);
for(int i=1;i<=N;++i){
scanf("%d",&bikes_number[i]);
}
int a,b,time;
for(int i=0;i<M;++i){
scanf("%d %d %d",&a,&b,&time);
G[a][b] = G[b][a] = time;
}
Dijkstra();
DFS(Sp);// 从终点开始递归访问
printf("%d ",min_need_sent_bike);
for(int i=result.size()-1;i>=0;--i){
printf("%d",result[i]);
if(i>0) printf("->");
}
printf(" %d",min_need_back_bike);
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。