题目大意:
一共有n头牛,有ml个关系好的牛的信息,有md个关系不好的牛的信息,对应输入的第一行的三个元素,接下来ml行,每行三个元素A,B,D,表示A牛和B牛相距不希望超过D,接下来md行,每行三个元素A,B,D表示A牛和B牛的相距至少要有D才行。求1号牛和n号牛的最大距离,如果距离无限大输出-2,如果无解输出-1。
思路1:
首先考虑吧,求的是1到n的最大距离,即
d[n] - d[1] <= t
可以看作以1为起点n为终点,移项得
d[n] <= d[1] + t
正好就是最短路中松弛过后的不等式的样子,因此相当于求1到n的最短路
简单多了,根据题目信息,有以下内容
a - b <= D
c - d >= D
转换为
a <= b + D
d <= c - D
这样转换是因为上面分析的思路是求解最短路,所以要转化成最短路的不等式,即
if(d[v] > d[u] + w(u, v)){
d[v] = d[u] + w[u, v];
}
松弛操作前:
d[v] > d[u] + w(u, v)
松弛操作后:
d[v] <= d[u] + w(u, v)
转换后,就可以建边了,分别是,b - > a 权值为 D 和 c - > d 权值为 -D的两个边
建好后以1为源点求最短路即可
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 51000;
int n, ml, md;
int head[N], nex[N], to[N], edge[N];
int cnt;
int c[N];
bool v[N];
int d[N];
struct SPFA {
SPFA() {
mem(head, -1);
mem(nex, -1);
mem(c, 0);
mem(v, 0);
cnt = 0;
mem(d, 0x3f);
}
void init() {
SPFA();
}
void add(int a, int b, int c) {
++cnt;
to[cnt] = b;
edge[cnt] = c;
nex[cnt] = head[a];
head[a] = cnt;
}
int spfa(int begin, int end) {
d[begin] = 0;
c[begin]++;
v[begin] = 1;
queue<int> q;
q.push(begin);
bool f = true;
while (!q.empty()) {
int t = q.front();
q.pop();
if (c[t] > n) {
f = 0;
break;
}
c[t]++;
v[t] = 0;
for (int i = head[t]; i != -1; i = nex[i]) {
int y = to[i];
int dist = edge[i];
if (d[y] > d[t] + dist) {
d[y] = d[t] + dist;
if (!v[y]) {
v[y] = 1;
q.push(y);
}
}
}
}
if (f) {
return d[end];
}
else return -404;
}
}sp;
int main()
{
ios::sync_with_stdio(0);
while (cin >> n >> ml >> md) {
sp.init();
for (int i = 0; i < ml; i++) {
int a, b, c;
cin >> a >> b >> c;
sp.add(a, b, c);
}
for (int i = 0; i < md; i++) {
int a, b, c;
cin >> a >> b >> c;
sp.add(b, a, -c);
}
int res = sp.spfa(1, n);
if (res == inf) {
cout << "-2\n";
}
else if (res == -404) {
cout << "-1\n";
}
else cout << res << "\n";
}
return 0;
}
思路2:
当然,也可以用最长路求解,第二个思路来说说最长路的解法
既然是1到n的最大距离,可以表示为
d[n] - d[1] <= t
转换为
d[1] - d[n] >= -t
进一步化为
d[1] >= d[n] - t
这是符合最长路求解中的松弛操作后的不等式
我们求的还是 t ,也就是说可以求 从n到1的最长路,我们求出来的结果是 -t (注意:是负的),只需要把 n 到 1 的最长路取相反数即可。
接下来就是和上面差不多的过程,建边的时候建立符合最长路松弛操作后的边,就不再赘述了,如果感兴趣可参阅上面尝试写一下,我就直接贴代码了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 51000;
int n, ml, md;
int head[N], nex[N], to[N], edge[N];
int cnt;
int c[N];
bool v[N];
int d[N];
struct SPFA {
SPFA() {
mem(head, -1);
mem(nex, -1);
mem(c, 0);
mem(v, 0);
cnt = 0;
mem(d, 0x3f);
for (int i = 0; i < N; i++)d[i] = -d[i];
}
void init() {
SPFA();
}
void add(int a, int b, int c) {
++cnt;
to[cnt] = b;
edge[cnt] = c;
nex[cnt] = head[a];
head[a] = cnt;
}
int spfa(int begin, int end) {
d[begin] = 0;
c[begin]++;
v[begin] = 1;
queue<int> q;
q.push(begin);
bool f = true;
while (!q.empty()) {
int t = q.front();
q.pop();
if (c[t] > n) {
f = 0;
break;
}
c[t]++;
v[t] = 0;
for (int i = head[t]; i != -1; i = nex[i]) {
int y = to[i];
int dist = edge[i];
if (d[y] < d[t] + dist) {
d[y] = d[t] + dist;
if (!v[y]) {
v[y] = 1;
q.push(y);
}
}
}
}
if (f) {
return d[end];
}
else return -404;
}
}sp;
int main()
{
ios::sync_with_stdio(0);
while (cin >> n >> ml >> md) {
sp.init();
for (int i = 0; i < ml; i++) {
int a, b, c;
cin >> a >> b >> c;
sp.add(b, a, -c);
}
for (int i = 0; i < md; i++) {
int a, b, c;
cin >> a >> b >> c;
sp.add(a, b, c);
}
int res = sp.spfa(n, 1);
if (res == -inf) {
cout << "-2\n";
}
else if (res == -404) {
cout << "-1\n";
}
else cout << -res << "\n";
}
return 0;
}