传送门
无向图,给定起点终点,找一条点权极值相差最小的路,在此基础上找最短路。
下面的代码思想是对差值进行二分,因为题目要求差值最小。差值最小是0,差值最大是输入的最大值减最小值。我们取某个差值作为限制条件,拿这个差值来限制最短路算法寻找下一个点的过程。
我想到的一种方法没有AC,而且至今没有想出问题所在,是这样的,把差值确定,但是具体的height
上下限不确定,这样去限制SPFA
算法,怎么在最短路算法中判断当前进行的路的差值呢?给每个点安排两个值,__min[]
和__max[]
,存放从起点走到当前点的最短路的极值,每当找下一个点时,判断下一个点的height
是否会破坏中介点存放的历史道路的差值。要说明的是,差值确定的意思是小于等于差值即可,所以某个差值不连通的话,更小的差值就不用试了。另外,考虑这种方法是因为觉得__min[]
和__max[]
的值是随着算法过程而递减和递增的,所以可以中途判断。
然而没有AC,所以肯定有问题。。感觉大概率是不能这么直接用一个差值来限制算法,判断传递过程可能存在逻辑错误,然而不知道测试数据也懒得想反例 。
(4月13日更新:已经想到反例。且看下图:
当当前差值是10
的时候,错误的方法(一遍SPFA
)找不到正确解,那两条虚线边意味着两种情况的无法到达。
找反例的时候先测试了一下错误方法,发现连可行性都判断不正确(在得到最后的l
以后又加了一个SPFA
循环,但是这样还是错的),也就是说,找到的l
都不正确,也就意味着必然有一种限制情况下本来可行,但是让它给判断成不可行了。果然,这种情况被我想出来了。实锤。)
正确的方法是不仅要有差值,还要给定一个下限,这样上限也确定了,范围就确定了,下限就枚举每个点的height
(正确答案肯定是以某个点的height
为底),这样每次二分就要枚举N
次SPFA
,得到一个同差值不同上下限的最小d[N]
;多次二分得到全局最优。因为有了上下限,所以最短路算法里直接可以根据height
排除下一个点了,逻辑也就比较清晰可证了,没再出什么幺蛾子。
下面放上正确代码,有几处还是要注意的,已注释。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
const int INF = 1e9;
const int MAXN = 101;
int N, M, T;
struct Edge
{
int n, w;
};
vector<Edge> ve;
vector<int> v[MAXN];
int height[MAXN];
int d[MAXN];
bool inq[MAXN];
int _min, _max;
int diff;
int ans;
int pre[MAXN];
void init()
{
_min = INF;
_max = -1;
ve.clear();
for (int i = 1; i <= N; i++) v[i].clear();
memset(pre, 0, sizeof pre);
diff = ans = 0; // 这句防止起点等于终点的极端情况,不然这两个都没有被显示赋初值就被输出去了。但其实想多了
}
void spfa(int s, int low, int high) // 只是定了一个明确的上下限
{
queue<int> q;
fill(inq + 1, inq + N + 1, false);
fill(d + 1, d + N + 1, INF);
if (height[s] < low || height[s] > high) return; // 先初始化完d[]再判断,因为调用返回后使用d[]的值
q.push(s);
inq[s] = true;
d[s] = 0;
for (; !q.empty();)
{
int t = q.front();
q.pop();
inq[t] = false;
for (int i = 0; i < v[t].size(); i++)
{
int n