题目链接:
题目大意:
把
n
个点在数轴上排成一排,并要求
先给出
ML
个关系
A,B,C(A<B)
要求
A
点和
数据范围:
2≤n≤10001≤ML≤100001≤MD≤10000
1≤A<B≤n1≤E<F≤n,1≤C,G≤106
解题思路:
典型的差分约束。
令
D[i]
为
i
号点所在的位置。
对于任一
ML
关系,则有
D[A]+C≥D[B]
,
对于任一
MD
关系,则有
D[E]+G≤D[F]
。
对上面三个式子进行变换,得:
D[i]–D[i+1]≤0
D[B]–D[A]≤C
D[E]–D[F]≤−G
那么,对于任意的
i(1≤i<n)
,从
(i+1)
到
i
建立一条边权为
对于任一的
ML
关系,从
A
到
以
1
号点为源点,跑最短路,若不存在负权回路,最后得出的
由于这道题可能存在负权回路,就不能用Dijkstra,可以用SPFA判负权回路。
对于SPFA判断负权回路,记一个
cnt[]
数组,
cnt[i]
表示
i
号节点进入队列的次数,若某一节点入队超过
这与Bellman-Ford判负权回路相类似。
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int MaxN = 1e4;
const int INF = 1 << 30;
int n, ml, md;
int all;
int pre[2 * MaxN + 5], last[MaxN + 5], other[2 * MaxN + 5];
int cost[2 * MaxN + 5];
int seq[MaxN + 5], dis[MaxN + 5];
int cnt[MaxN + 5]; //一个点的入队次数
bool inque[MaxN + 5];
void build(int x, int y, int w)
{
pre[++all] = last[x];
last[x] = all;
other[all] = y;
cost[all] = w;
}
int SPFA(int s)
{
for(int i = 1; i <= n; i++) dis[i] = INF;
dis[s] = 0;
int head = 1, tail = 0;
seq[++tail] = s;
inque[s] = 1;
int now, ed, dr;
while(head <= tail) {
now = seq[head];
ed = last[now];
while(ed != -1) {
dr = other[ed];
if(dis[now] + cost[ed] < dis[dr]) {
dis[dr] = dis[now] + cost[ed];
if(inque[dr] == 0) {
cnt[dr]++;
if(cnt[dr] > n) return -1;
//检测是否存在负权回路
inque[dr] = 1;
seq[++tail] = dr;
}
}
ed = pre[ed];
}
inque[now] = 0;
head++;
}
if(dis[n] == INF) return -2;
else return dis[n];
}
int main()
{
while(scanf("%d %d %d", &n, &ml, &md) != EOF)
{
all = -1; memset(last, -1, sizeof(last));
//对于任意的d[x] - d[y] <= z,建立y -> x 边权为z的有向边
for(int i = 1; i <= ml; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
//u为编号较小点
if(u > v) swap(u, v);
build(u, v, w);
//d[u] + w >= d[v] ==> d[v] - d[u] <= w
//建立u -> v 边权为w的有向边
}
for(int i = 1; i <= md; i++) {
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
//u为编号较小点
if(u > v) swap(u, v);
build(v, u, -w);
//d[u] + w <= d[v] ==> d[u] - d[v] <= -w
//建立v -> u 边权为-w的有向边
}
int ans = SPFA(1);
printf("%d\n", ans);
}
return 0;
}