差分约束 虚拟源点 T4 #10090. 「一本通 3.4 练习 2」布局 Layout - 题目 - LibreOJ (loj.ac)

原题来自:USACO 2005 Dec. Gold

FJ 有 N 头奶牛 ,编号为 。奶牛们将按照编号顺序排成一列队伍(可能有多头奶牛在同一位置上)。换句话说,假设  号奶牛位于 ,则 

有些奶牛是好基友,它们希望彼此之间的距离小于等于某个数。有些奶牛是情敌,它们希望彼此之间的距离大于等于某个数。

给出  对好基友的编号,以及它们希望彼此之间的距离小于等于多少;又给出  对情敌的编号,以及它们希望彼此之间的距离大于等于多少 

请计算:如果满足上述所有条件, 号奶牛和  号奶牛之间的距离最大为多少。

输入格式

第一行:三个整数 ,用空格分隔。
第  行:每行三个整数 ,用空格分隔,表示 
第  行:每行三个整数 ,用空格分隔,表示 
保证 

输出格式

一行,一个整数。如果没有合法方案,输出 . 如果有合法方案,但  号奶牛可以与  号奶牛相距无穷远,输出 . 否则,输出  号奶牛与  号奶牛间的最大距离。

样例

输入

4 2 1
1 3 10
2 4 20
2 3 3

输出

27

数据范围

对于全部数据,

思路

的距离, 可以根据题目得出一些系列不等式:

  1. , 即
  2. , 即

再检验当前0点是否可以到达所有点, 因为条件一是从 , 故从0点不能到达所有点, 可以假设一个无限远地方有一个点, 连到所有点的权值为0, 也就是定义一个虚拟源点。实际上我们也不需要真的建边, 只需要在SPFA时把所有点加入队列中就行。

题目要求先判断负环, 那么需要把所有点加入其中。 然后判断是否可以无穷远, 图论中对应无穷远就是不存在从1到n的边, 我们只需要再做一次从1出发的SPFA即可, 若 则输出-2。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
const int N = 1e3 + 10, M = 3e6;
int h[N], e[M], w[M], ne[M], idx;
typedef long long LL;
LL dist[N];
int q[N], cnt[N];
int n, m1, m2;
bool st[N];
 
void add(int a, int b, int c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; }
 
bool spfa(int size)
{
    memset(cnt, 0, sizeof cnt);
    memset(st, 0, sizeof st);
    memset(dist, 0x3f, sizeof dist);
    int hh = 0, tt = 0;
    for (int i = 1; i <= size; i++)
    {
        q[tt++] = i;
        dist[i] = 0;
        st[i] = true;
    }
    while (hh != tt)
    {
        int t = q[hh++];
        if (hh == N)
            hh = 0;
        st[t] = false;
 
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if (cnt[j] >= n + 1)
                    return false;
                if (!st[j])
                {
                    st[j] = true;
                    q[tt++] = j;
                    if (tt == N)
                        tt = 0;
                }
            }
        }
    }
    return true;
}
 
int main()
{
    memset(h, -1, sizeof h);
    cin >> n >> m1 >> m2;
    for (int i = 1; i < n; i++)
        add(i + 1, i, 0);
 
    while (m1--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    while (m2--)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(b, a, -c);
    }
 
    if (!spfa(n))
        cout << -1 << endl;
    else
    {
        spfa(1);
        if (dist[n] >= 0x3f3f3f3f / 2)
            cout << "-2" << endl;
        else
            cout << dist[n] << endl;
    }
 
    return 0;
}