题意
- 给定图(城市,路),给定边权(路程),点权(救援队数目),求两点间最短路条数(边权和最小),并求其中点权和的最大值。
注意
fill
函数初始化二维数组的写法和陷阱。Dijkstra
算法的扩展:
- 在
<
和==
处理上,注意最短路数目在==
情况下可直接加。 - 为每个点设两个数组,分别用于保存起点到该点的最短路数目和点权和。
- 边权和最小值与点权和最大值在路径上可传递,满足最优性原理。
- 证明:设
r
为s
到t
的一条最短路且是在所有从s
到t
的最短路中点权和最大的最短路,设t
的前驱结点t0
,则在r
中s
到t0
的路径r0
也是如此。不然可找到一条s
到t0
的路径r1
,r1
要么比r0
更短要么和r0
一样短但点权和比r0
更大,则将r1
替换r0
,可得原假设r
不成立,所以矛盾,所以原命题成立。 - 最初使用的
INT_MAX
,后改用int INF=1e9
表示最大值。 - 没有把
u
初始化为-1。 - 这里的所谓 点权和最大 是建立在该点的最短路基础上的,想想如果点权和最大的最短路也不是唯一的,怎么求其数目?
- 最优性原理:最优策略的子序列(连续、顺序)在其子问题上也是最优的。
单词
- scatter 分散
代码
#include <iostream>
#include <climits>
using namespace std;
const int MAX = 500;
int n, m;
int s, t;
int v_wei[MAX];
int e_wei[MAX][MAX];
int road[MAX];
int v_max[MAX];
int e_min[MAX];
bool S[MAX];
void Dijkstra(int s0)
{
fill(S, S + n, false);
fill(e_min, e_min + n, INT_MAX);
v_max[s0] = v_wei[s0];
e_min[s0] = 0;
road[s0] = 1;
for (int i = 0; i < n; i++)
{
int min = INT_MAX;
int u;
for (int j = 0; j < n; j++)
{
if (!S[j] && e_min[j] < min)
{
min = e_min[j];
u = j;
}
}
S[u] = true;
for (int k = 0; k < n; k++)
{
if (!S[k] && e_wei[u][k] != INT_MAX)
{
if (e_min[u] + e_wei[u][k] < e_min[k])
{
road[k] = road[u];
e_min[k] = e_min[u] + e_wei[u][k];
v_max[k] = v_max[u] + v_wei[k];
}
else if (e_min[u] + e_wei[u][k] == e_min[k])
{
road[k] += road[u];
if (v_max[k] < v_max[u] + v_wei[k])
v_max[k] = v_max[u] + v_wei[k];
}
}
}
}
}
int main()
{
scanf_s("%d%d%d%d", &n, &m, &s, &t);
for (int i = 0; i < n; i++)
scanf_s("%d", &v_wei[i]);
fill(e_wei[0], e_wei[0] + MAX*MAX, INT_MAX);
int v1, v2;
for (int i = 0; i < m; i++)
{
scanf_s("%d%d",&v1,&v2);
scanf_s("%d", &e_wei[v1][v2]);
e_wei[v2][v1] = e_wei[v1][v2];
}
Dijkstra(s);
printf("%d %d", road[t], v_max[t]);
return 0;
}