版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_26398495/article/details/82078697
题目链接
题目分析
给出结点信息,输出最短路径;
总站编号为0,其他站点编号为1-N
多条最短路径时,其他标尺:
在最短路径过程中,必须把每个结点的权值调整到题目要求的最佳;
- 标尺一:选择需要从总站带出最少量的路径
- 标尺二:仍有多条,则选择需要带回最少量的路径
- 【陷阱】:不能在返程过程中才调整结点,去的时候就需要调整好数量,否则两个测试点错误(25`)
解题思路
用 Dijkstra()
算法 查找所有最短路径并保存,之后用DFS()
筛选;
筛选策略:由于不满足最优子结构,回溯到起点,只能正向枚举完整路径(vector<int>
),逐个结点计算需求量和带回量!
测试数据
10 4 4 5
4 8 9 0
0 1 1
1 2 1
1 3 2
2 3 1
3 4 1
输出: 1 0->1->2->3->4 2
AC程序(C++)
/**********************************
*@ID: 3stone
*@ACM: PAT.A1018 Emergency
*@Time: 18/8/20
*@IDE: VSCode 2018 + clang++
***********************************/
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 505;
const int INF = 0x7fffffff;
int G[maxn][maxn]; //图
int d[maxn]; //起点到此点的距离
vector<int> pre[maxn]; //记录前驱结点
int vis[maxn]; //标记是否访问
int weight[maxn]; //点的权重
int N, M, capacity, Ed, perfect_num; //结点数、边数、最大容量、
void Dijkstra(int s) {
//总站点 初始化 weight[s]不需要初始化了,因为用不到
d[s] = 0;
for(int i = 0; i < N; i++) {
int u = -1, MIN = INF;
for(int j = 0; j <= N; j++) {
if(vis[j] == false && d[j] < MIN){
u = j;
MIN = d[j];
}
}
if(u == -1) return;
vis[u] = true;
for(int v = 0; v <= N; v++) {
if(vis[v] == false && G[u][v] != INF){
if(d[u] + G[u][v] < d[v]) {
d[v] = d[u] + G[u][v];
pre[v].clear();
pre[v].push_back(u);
} else if (d[u] + G[u][v] == d[v]) {
pre[v].push_back(u); //权重更小,换路
}
}//if
}//for - v
}//for - i
}//Dijkstra
int min_needed, min_remain; //需求量,带回量
vector<int> best_route; //最佳最短路径
void dfs_route(int ed, vector<int> cur_route) {
if(ed == 0) { //回溯到总站,cur_route保存了一条最短路径
/* 由于不满足最优子结构,要计算需要带出多少,只能到达起点,正向枚举最短路径每个结点
*/
//cur_route.push_back(ed); //起点不需要加入路径不是吗?
int needed = 0, remain = 0;
//正向枚举路径
for(int i = cur_route.size() - 1; i >= 0; i--){
int id = cur_route[i];
if(weight[id] > 0) {
remain += weight[id];
} else if(weight[id] < 0) {
if(remain >= abs(weight[id])) {
remain += weight[id];
} else {
needed += (abs(weight[id]) - remain);
remain = 0;
}
}
}
if(needed < min_needed) {
min_needed = needed;
min_remain = remain;
best_route = cur_route;
} else if(needed == min_needed && remain < min_remain) {
min_remain = remain;
best_route = cur_route;
}
return;
}
//枚举每条最短路径点
cur_route.push_back(ed);
for(int i = 0; i < pre[ed].size(); i++) {
dfs_route(pre[ed][i], cur_route);
}
cur_route.pop_back();
}
int main() {
int c1, c2, L;
while(scanf("%d%d%d%d", &capacity, &N, &Ed, &M) != EOF) {
fill(d, d + maxn, INF); //最短距离
fill(vis, vis + maxn, false); //标记访问
fill(G[0], G[0] + maxn * maxn, INF); //边权
perfect_num = capacity / 2;
//输入结点权重(当前车辆数)
for(int i = 1; i <= N; i++) {
scanf("%d", &weight[i]);
weight[i] -= perfect_num; //把点权更新为该站点车辆的需求量,正为溢出,负为缺少
}
//输入边信息
for(int i = 0; i < M; i++) {
scanf("%d%d%d", &c1, &c2, &L);
G[c1][c2] = L; //无向图转为 双向边
G[c2][c1] = L;
}
Dijkstra(0);
min_needed = INF;
min_remain = INF;
best_route.clear();
vector<int> cur_route;
dfs_route(Ed, cur_route);
printf("%d 0->", min_needed); //需求量
for(int i = best_route.size() - 1; i > 0; i--) {
printf("%d->", best_route[i]);
}
printf("%d", best_route[0]);
printf(" %d\n", min_remain); //带回量
}
return 0;
}