Layout·差分约束系统 Bellman_Ford SPFA

该博客介绍了如何解决一个关于奶牛排队的问题,其中涉及到奶牛之间的距离约束。奶牛们希望彼此保持一定的距离,而算法需要找出满足所有约束的最大可能距离。博主探讨了两种算法——Bellman_Ford和SPFA,将问题转化为求解最短路问题,并给出了相应的代码实现。这两种算法都是用于寻找图中两点间最短路径的经典方法。
摘要由CSDN通过智能技术生成

题目信息

Like everyone else, cows like to stand close to their friends when queuing for feed. FJ hasN (2 <= N <= 1000)cows numbered 1...N standing along a straight line waiting for feed. The cows are standing in the same order as they are numbered, and since they can be rather pushy, it is possible that two or more cows can line up at exactly the same location (that is, if we think of each cow as being located at some coordinate on a number line, then it is possible for two or more cows to share the same coordinate).
Some cows like each other and want to be within a certain distance of each other in line. Some really dislike each other and want to be separated by at least a certain distance. A list of ML ( 1 <= ML <= 10000)constraints describes which cows like each other and the maximum distance by which they may be separated; a subsequent list of MD constraints(1 <= MD <= 10000) tells which cows dislike each other and the minimum distance by which they must be separated.
Your job is to compute, if possible, the maximum possible distance between cow 1 and cow N that satisfies the distance constraints.
和其他人一样,奶牛在排队领取饲料时喜欢站得离朋友很近。FJ有N (2 <= N <= 1000)1...N的奶牛站在一条直线上等待喂食。奶牛站在同一个顺序,因为他们是编号的,因为他们可以相当积极,有可能两个或更多的奶牛可以在完全相同的位置排队(也就是说,如果我们认为每头奶牛都位于某个坐标上的数字线,那么有可能两个或更多的奶牛共享同一坐标)。
有些奶牛喜欢彼此,希望在一定距离内排队。有些人真的不喜欢对方,希望至少分开一段距离。ML ( 1 <= ML <= 10000)约束列表描述了哪些奶牛彼此喜欢,以及它们可以分开的最大距离;MD约束的后续列表(1 <= MD <= 10000)说明了哪些奶牛彼此不喜欢,以及它们必须分开的最小距离。
如果可能的话,您的工作是计算奶牛1和奶牛N之间满足距离约束的最大可能距离。

输入

Line 1: Three space-separated integers: N, ML, and MD.
Lines 2…ML+1: Each line contains three space-separated positive integers: A, B, and D, with 1 <= A < B <= N. Cows A and B must be at mostD (1 <= D <= 1,000,000)apart.
Lines ML+2…ML+MD+1: Each line contains three space-separated positive integers: A, B, and D, with 1 <= A < B <= N. Cows A and B must be at leastD (1 <= D <= 1,000,000)apart.
第1行:三个空格分隔的整数:N、ML和MD。
行2…ML+1:每行包含三个空格分隔的正整数:A、B和D,其中1 <= A < B <= N。A和B之间的间隔不得超过D (1 <= D <= 1000000)
行ML+2…ML+MD+1:每行包含三个空格分隔的正整数:A、B和D,1 <= A < B <= N。A和B必须至少相隔D ( 1<= D <= 1000000)

输出

Line 1: A single integer.
If no line-up is possible, output -1. If cows 1 and N can be arbitrarily far apart, output -2. Otherwise output the greatest possible distance between cows 1 and N.
第1行:一个整数。
如果无法进行排列,则输出-1。
如果牛1和牛N可以任意相隔很远,则输出-2。
否则输出牛1和牛N之间的最大可能距离。

测试样例

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

提示

There are 4 cows. Cows #1 and #3 must be no more than 10 units apart, cows #2 and #4 must be no more than 20 units apart, and cows #2 and #3 dislike each other and must be no fewer than 3 units apart.
The best layout, in terms of coordinates on a number line, is to put cow #1 at 0, cow #2 at 7, cow #3 at 10, and cow #4 at 27.
有4头牛。1号和3号母牛相距不得超过10个单位,2号和4号母牛相距不得超过20个单位,2号和3号母牛彼此不喜欢,相距不得少于3个单位。
根据数字线上的坐标,最好的布局是将cow#1放在0,cow#2放在7,cow#3放在10,cow#4放在27。

来源

USACO 2005 December Gold

解答

方法一·Bellman_Ford

#include <iostream>

using namespace std;
#define INF 0xffffff//不能使用INT_MAX,相加的时候会溢出
#define MaxP 1005//最大奶牛数
#define MaxE 10005//最大限制数
int N, ML, MD;
int id;//总边数

int dis[MaxP];
struct Edge
{
    int u;
    int v;
    int w;
} edges[MaxE];

void AddEdge(int u, int v, int w)
{//加边
    edges[id].u = u;
    edges[id].v = v;
    edges[id].w = w;
    id++;
}

void Bellman_Ford()
{
    dis[1] = 0;

    for (int i = 2; i <= N; i++)
    {//先将所有点至s(即1)距离设置为最大值
        dis[i] = INF;
    }

    for (int i = 1; i < N; i++)
    {//对每个点进行遍历
        for (int j = 0; j < id; j++)
        {//遍历所有的边
            if (dis[edges[j].v] > dis[edges[j].u] + edges[j].w)
            {//松弛操作,算出0到v的距离
                dis[edges[j].v] = dis[edges[j].u] + edges[j].w;
            }
        }
    }

    //判断是否出现负环,如果出现负环则无解
    for (int j = 0; j < id; j++)
    {//最短路一定是不允许出现负环的,否则最短路的寻找便会一直围绕着负环不断旋转
        if (dis[edges[j].v] > dis[edges[j].u] + edges[j].w)
        {
            cout << -1 << endl;
            return;
        }
    }

    //如果dis[N]==INF,说明1到N可以是任意值
    if (dis[N] == INF)
    {//最后的牛没有被定义
        cout << -2 << endl;
    }
	else
	{//如果有解,输出dis[N]
        cout << dis[N] << endl;
    }
    return;
}

int main()
{
    freopen("E://test.txt", "r", stdin);
    cin >> N >> ML >> MD;
    int u, v, w;
    for (int i = 0; i < ML; i++)
    {
        cin >> u >> v >> w;
        AddEdge(u, v, w);
    }
    for (int i = 0; i < MD; i++)
    {
        cin >> u >> v >> w;
        AddEdge(v, u, -w);
    }

    Bellman_Ford();
    return 0;
}

方法二·SPFA

#include <iostream>
#include <queue>

using namespace std;

#define INF 0xfffffff
#define MaxP 1005//最大奶牛数
#define MaxE 10005//最大限制数

int N, ML, MD, id;

struct Node
{
    int to;
    int w;
    int next;
} edges[MaxE];

int dis[MaxP];//与起始点的距离
int outque[MaxP];//记录出队了几次
int head[MaxP];//记录当前点连接的是第几条边
bool vis[MaxP];//是否为第一次遍历到

void AddEdge(int u, int v, int w)
{
    edges[id].to = v;//被指向的点
    edges[id].w = w;//权值
    edges[id].next = head[u];//头部节点,这样可以使得从某点出发的节点那些点串联起来
    head[u] = id++;//用来记录当前u点连接的是第几条边
}

void SPFA()
{
    int flag = 0;
    for (int i = 1; i <= N; i++)
    {
        dis[i] = INF;
    }
    dis[1] = 0;

    queue<int> q;

    q.push(1);
    vis[1] = true;//先压入第一个点,并且将第一个点的阅览状态标记为已读

    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = false;

        outque[u]++;//用来记录着出队了几次
        if (outque[u] > N + 1)//如果一个点出队大于N次,则出现负环
        {
            flag = 1;
            break;
        }

        for (int i = head[u]; i != -1; i = edges[i].next)
        {//遍历出此时的点所指向其他点的边,head[u]就能立马寻找到你是id为多少的那个边
            int tmp = dis[u] + edges[i].w;
            if (dis[edges[i].to] > tmp)
            {
                dis[edges[i].to] = tmp;
                if (!vis[edges[i].to])
                {
                    vis[edges[i].to] = true;
                    q.push(edges[i].to);
                }
            }
        }
    }

    if (flag == 1)
    {
        cout << -1 << endl;
        return;
    }
    if (dis[N] == INF)
    {
        cout << -2 << endl;
    }
    else
    {
        cout << dis[N] << endl;
    }
    return;
}

int main()
{
    freopen("E://test.txt", "r", stdin);
    int u, v, w;
    cin >> N >> ML >> MD;
    for (int i = 0; i < MaxP; i++)
    {
        head[i] = -1;
    }
    for (int i = 0; i < ML; i++)
    {
        cin >> u >> v >> w;
        AddEdge(u, v, w);
    }
    for (int i = 0; i < MD; i++)
    {
        cin >> u >> v >> w;
        AddEdge(v, u, -w);
    }

    SPFA();
    return 0;
}

想法

将此问题转换为最短路问题:
假设有一下三个条件:
x1 - x2 <= k1
x2 - x3 <= k2
x1 - x3 <= k3
那么我们可以将不等式1和不等式2相加得到:
x1 - x3 <= k1 + k2
x1 - x3 <= k3
那么想要得到x1和x3的最大差值,就是取min(k1 + k2, k3)
在这里插入图片描述
那么就可以根据不等式建立有向图,通过有向图计算1号点到n号点的最短路,其值就是x1与xn的最大差值。

回到这个题中:
ml条消息:牛A和牛B的距离不想超过D,那么建立不等式:posA - posB <= D加入到图中直接add(A, B, D)即可。
md条信息:牛A和牛B的距离至少要为D,那么建立不等式:posA - posB >= D,那么我们左右两边同乘-1有:posB - posA <= -D,那么加入到图中add(B, A, -D)即可。

图建立好之后直接计算最短路即可。
对应输出:
1.如果dis【n】==inf,输出-2;
2.否则输出dis【n】;
3.如果在发现了负环,说明问题无解,那么输出-1.

需注意奶牛是按顺序站好的,意味着从1到N是按顺序排列的

SPFA:
在这里插入图片描述
比如上述的图中:
首先建立起始点a到其余点的最短路径表格

abcdefg
d[i]0
点a先进入对列,当队列不空的时候队首进入队列中,对以a为起始点的所有边的终点依次进行松弛操作,此时有b, c, d三个点,此时的表格被更新为:
abcdefg
d[i]024815
更新的这三个点在队列中从来都没有出现过,这些点都需要入队,此时队中有三个节点b, c, d
队首元素b出队,对以b为起始点的终点依次进行松弛操作,此时只有e点,更新表格:
abcdefg
d[i]02481530
对以c为起始点的终点依次进行松弛操作,此时有e, f点,更新表格:
abcdefg
d[i]0248151511
此时队列中是d, e, f

d出队,此时有g点,更新表格:

abcdefg
d[i]024815151119
此时队列中是e, f, g

e出队,此时e只能用来更新g点,但是g点松弛不成功,无法更短,所以直接e出队不用更新表格,队列中是f, g

f出队,此时有e, d, g点,但只有e, g被松弛了,更新表格:

abcdefg
d[i]024815131114
g在队列中不用再添加,只用向队列中添加没有的e,此时队列中只剩下了g, e

g出队,此时有b点被松弛,更新表格:

abcdefg
d[i]017815131114

最后e出队,仍然是只能更新g点,但是g点仍然松弛不成功,无法更短,所以结束得到答案14

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhj12399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值