题意
N
个车站和一个管理中心,给定终点,选取管理中心到终点的最短路径,若有多条最短路径,要根据一些奇怪的标准从中选取一条唯一的最短路径(首先最小take
其次最小return
)。
注意
- 这道题肯定先要用
dijkstra
,然后问题来了,take
和return
值在路径上不满足传递性!这就是说,给定终点x
和到该点的最优路径0~x
(最短路径里按照标准选取的唯一),其子问题(终点为0~x
中间某点y
)的最优路径不一定是0~x
中的0~y
,反例如下。
10 4 4 5 //原问题:0~4 //子问题:0~3
4 8 9 0
0 1 1
1 2 1
1 3 2
2 3 1
3 4 1
- 这道题还有个陷阱,其实没有说清。如下,车站的调整必须是从中心出发到目的车站连续的过程,返程不能调整。
Finally after another space, output the number of bikes that we must take back to PBMC after the condition of Sp is adjusted to perfect.
- 因为不满足传递性,所以不能在
dijkstra
中的更新路径部分应用标准,所以,要么记录前驱,之后dfs
遍历多条最短路径;要么记录完整的最短路径,这样的话需要vector<vector<int>> p[Nmax]
,每个结点占有一个二维数组。
代码
#include <iostream>
#include <algorithm>
#include <vector>
#include <climits>
using namespace std;
const int Nmax = 501;
int Cmax, N, Sp, M, C2;
int bike[Nmax];
int road[Nmax][Nmax];
int take, rtn;
int dist[Nmax];
bool S[Nmax];
vector<vector<int>> p[Nmax];
void DIJKSTRA(int s0)
{
fill(S, S + Nmax, false);
fill(dist, dist + Nmax, INT_MAX);
dist[0] = 0;
vector<int> v;
v.push_back(0);
p[0].push_back(v);
for (int j = 0; j <= N; j++)
{
int min = INT_MAX;
int k;
for (int i = 0; i <= N; i++)
{
if (!S[i] && dist[i] < min)
{
min = dist[i];
k = i;
}
}
S[k] = true;
for (int i = 0; i <= N; i++)
{
if (!S[i] && road[k][i] != INT_MAX)
{
if (dist[k] + road[k][i] < dist[i])
{
dist[i] = dist[k] + road[k][i];
p[i].clear();
p[i] = p[k];
for (int l = 0; l < p[i].size(); l++)
p[i].at(l).push_back(i);
}
else if (dist[k] + road[k][i] == dist[i])
{
for (int l = 0; l < p[k].size(); l++)
{
p[i].push_back(p[k].at(l));
p[i].at(p[i].size() - 1).push_back(i);
}
} 多条最短路径全部记录
}
}
}
}
int select_optimal_road(int sp)
{
int opt = -1;
take = rtn = INT_MAX;
vector<vector<int>> v = p[sp];
for (int i = 0; i < v.size(); i++)
{
vector<int> v0 = v.at(i);
int tk = 0, rt = 0;
for (int j = 1; j < v0.size(); j++)
{
if (bike[v0.at(j)] > C2)
rt += bike[v0.at(j)] - C2;
else if(bike[v0.at(j)] < C2)
{
if (rt > C2 - bike[v0.at(j)])
rt -= C2 - bike[v0.at(j)];
else
{
tk += C2 - bike[v0.at(j)] - rt;
rt = 0;
}
}
}
if (tk < take)
{
opt = i;
take = tk;
rtn = rt;
}
else if (tk == take && rt < rtn)
{
opt = i;
rtn = rt;
}
}
return opt;
}
int main()
{
cin >> Cmax >> N >> Sp >> M;
C2 = Cmax / 2;
for (int i = 1; i <= N; i++)
cin >> bike[i];
int r1, r2;
fill(road[0], road[0] + Nmax*Nmax, INT_MAX);
for (int i = 0; i < M; i++)
{
cin >> r1 >> r2;
cin >> road[r1][r2];
road[r2][r1] = road[r1][r2];
}
DIJKSTRA(0);
int o = select_optimal_road(Sp);
vector<int> v = p[Sp].at(o);
cout << take << " ";
for (int i = 0; i < v.size(); i++)
{
if (i == 0)
cout << v.at(i);
else cout << "->" << v.at(i);
}
cout << " " << rtn;
return 0;
}
dfs
版本,注意这个dfs
写法很有讲究
4月10日更新:这道题是需要对一条完整的最短路进行分析,不能中途比较。我们在最短路算法中得到了每个点的前驱表(都是最短路),然后就写一个dfs
从终点出发,到了起点才能得到一条完整的最短路,然后再分析这条路。所以这个temp
就用来存放dfs
的当前行程。所以就有了两种写法(PAT A1004),temp
存储 / 弹出当前点或者存储 / 弹出当前点的前驱点。下面的代码两种写法都有了,其中把前者注释掉了。
#include <iostream>
#include <algorithm>
#include <vector>
#include <climits>
using namespace std;
const int Nmax = 501;
int Cmax, N, Sp, M, C2;
int bike[Nmax];
int road[Nmax][Nmax];
int take = 1e9, rtn = 1e9;
int dist[Nmax];
bool S[Nmax];
vector<int> r[Nmax];
vector<int> ans, temp;
void DIJKSTRA(int s0)
{
fill(S, S + Nmax, false);
fill(dist, dist + Nmax, INT_MAX);
dist[0] = 0;
for (int j = 0; j <= N; j++)
{
int min = INT_MAX;
int k;
for (int i = 0; i <= N; i++)
{
if (!S[i] && dist[i] < min)
{
min = dist[i];
k = i;
}
}
S[k] = true;
for (int i = 0; i <= N; i++)
{
if (!S[i] && road[k][i] != INT_MAX)
{
if (dist[k] + road[k][i] < dist[i])
{
dist[i] = dist[k] + road[k][i];
r[i].clear();
r[i].push_back(k);
}
else if (dist[k] + road[k][i] == dist[i])
r[i].push_back(k);
}
}
}
}
void select_optimal_road(int n) // 正向计算中途不能和其他支路(也是最短路)比较,必须单独一条路算完之后再比较
{ // 该问题只支持正向计算(求出最短路后)
//temp.push_back(n);
if (n == 0) // 终于回到起点了,这样构成了一条完整的最短路
{
int tk = 0, rt = 0;
for (int i = temp.size() - 2; i >= 0; i--)
{
if (bike[temp[i]] >= C2)
rt += bike[temp[i]] - C2;
else
{
if (rt >= C2 - bike[temp[i]])
rt -= C2 - bike[temp[i]];
else
{
tk += C2 - bike[temp[i]] - rt;
rt = 0;
}
}
}
if (tk < take)
{
take = tk;
rtn = rt;
ans = temp;
}
else if (tk == take && rt < rtn)
{
rtn = rt;
ans = temp;
}
//temp.pop_back();
return;
}
for (int i = 0; i < r[n].size(); i++)
{
temp.push_back(r[n][i]);
select_optimal_road(r[n][i]);
temp.pop_back();
}
//temp.pop_back();
}
int main()
{
cin >> Cmax >> N >> Sp >> M;
C2 = Cmax / 2;
for (int i = 1; i <= N; i++)
cin >> bike[i];
int r1, r2;
fill(road[0], road[0] + Nmax*Nmax, INT_MAX);
for (int i = 0; i < M; i++)
{
cin >> r1 >> r2;
cin >> road[r1][r2];
road[r2][r1] = road[r1][r2];
}
DIJKSTRA(0);
temp.push_back(Sp);
select_optimal_road(Sp);
temp.pop_back(); // 这句可有可无
cout << take << " ";
for (int i = ans.size() - 1; i >= 0; i--)
{
if (i == 0)
cout << ans[i];
else cout << ans[i] << "->";
}
cout << " " << rtn;
return 0;
}