poj3169:Layout——差分约束+Bellman-Ford算法

差分约束系统

参考:https://www.cnblogs.com/genius777/p/9163103.html

差分约束系统只是对最短路算法的一种应用,没有什么新的算法,只是对于具体问题的建图方法的确定

差分约束系统解决的问题是不等式组的求解:

X1 - X2 <= 0
X1 - X5 <= -1
X2 - X5 <= 1
X3 - X1 <= 5
X4 - X1 <= 4
X4 - X3 <= -1
X5 - X3 <= -3
X5 - X4 <= -3

这就是一个不等式组,给出的不等式组只存在小于等于号,如果有个别的式子是大于等于,我们可以通过两边同时乘-1的得到统一的不等号方向的不等式组。

这个不等式组一定是无解和无数解两种情况,因为如果是存在任意一组解,{x1,x2,x3,x4,x5},我们都可以通过{x1+k,x2+k,x3+k,x4+k,x5+k}得到一个新的解。所以解的个数是无数的。

因为每个数都加k,他们任意两个数之间的差是不变的,所以对于不等式没有影响。

与最短路联系

B - A <= c     (1)
C - B <= a     (2)
C - A <= b     (3)

如果要求C-A的最大值,可以知道max(C-A)= min(b,a+c),而这正对应了下图中C到A的最短路。

img

差分约束建图技巧

1.对于一个全部都是<=号的不等式组,我们可以将每个式子转化为Xi<=Xj+W(i,j),那么就建一条边,Xj->Xi=W(i,j),然后利用最短路径解决问题,在x0定死的情况下,求得最小值

2.对于一个全部都是>=号的不等式组,我们可以将每个式子转化为Xi>=Xj+W(i,j),那么就建一条边,Xj->Xi=W(i,j),然后利用最长路径解决问题,在x0定死的情况下,求得最大值

如果dis[Xi]为inf或-inf,那么Xi为任意解

如果求最短路的过程中出现负环,那么说明该不等式组无解

题目描述

需要放置n头牛,AL和BL两头牛之间存在“喜欢”关系,则它们之间的距离不能超过DL,有些AD和BD两头牛之间存在“不喜欢”关系,则它们之间的距离不能超过DD。所有牛编号从1~N,求1号牛和N号牛之间的最大距离,无解输出-1,距离无限大输出-2

分析

根据条目分析,可以得出约束条件为,d[i]为第i头牛放置的位置

  • d[i+1] + 0 >= d[i],n头牛按编号顺序依次放置,且可放置在同一位置
  • d[AL] + DL >= d[BL],“喜欢”关系的两头牛之间距离不超过DL
  • d[BD] - DD >= d[AD],“不喜欢”关系两头牛之间的距离不小于DD

将原问题转化成最短路问题,

  • 建立一条从i+1到i,权值为0的边
  • 建立一条从AL到BL,权值为DL的边
  • 建立一条从BD到AD,权值为-DD的边

原问题d[N] - d[1]的最大值,对应顶点1到顶点N的最短距离(不理解可看上文差分约束与最短路径关系部分),由于图中存在负权重的边,不能使用Dijkstra算法,可采用Bellman-Ford算法求解。

代码

#include <stdio.h>
#include <iostream>
#include <climits>
#include <vector>
using namespace std;

const int MAXN = 1000 + 10;
const int MAXM = 10000 + 10;
const int INF = INT_MAX;

int N, ML, MD;
int AL[MAXM], BL[MAXM], DL[MAXM];   //'喜欢'关系约束
int AD[MAXM], BD[MAXM], DD[MAXM];   //'不喜欢'关系约束
int dist[MAXN];     //最短距离

//邻接表存储图
struct Edge{
    int to, weight;
    Edge(int t, int w): to(t), weight(w) {}
};
vector<Edge> G[MAXN];

//根据差分约束建立图
void BuildGraph(){
    //从i+1->i权值为0的边
    for (int i = 1; i < N; ++i) {
        G[i+1].push_back(Edge(i, 0));
    }

    /*
         * BL - AL <= DL
         * AL + DL >= BL
         * 从AL到BL权值为DL的边 */
    for (int i = 0; i < ML; ++i) {
        G[AL[i]].push_back(Edge(BL[i], DL[i]));
    }

    /*
         * BD - AD >= DD
         * BD - DD >= AD
         * 从BD到AD权值-DD的边*/
    for (int i = 0; i < MD; ++i) {
        G[BD[i]].push_back(Edge(AD[i], -DD[i]));
    }
}

void Bellman_Ford(){
    fill(dist, dist + MAXN, INF);
    dist[1] = 0;

    //循环N-1次
    for (int i = 0; i < N - 1; ++i) {
        //对所有边进行松弛操作
        for (int j = 1; j <= N; ++j) {
            for (int k = 0; k < G[j].size(); ++k) {
                Edge e = G[j][k];
                if (dist[j] < INF && dist[e.to] > dist[j] + e.weight){
                    dist[e.to] = dist[j] + e.weight;
                }
            }
        }
    }

    //再遍历一次所有边(第N次循环)
    bool ngloop = false;
    for (int j = 1; j <= N; ++j) {
        for (int k = 0; k < G[j].size(); ++k) {
            Edge e = G[j][k];
            //如果本次有更新,则存在负环
            if (dist[j] < INF && dist[e.to] > dist[j] + e.weight){
                dist[e.to] = dist[j] + e.weight;
                ngloop = true;
            }
        }
    }

    if (ngloop){
        printf("-1\n");
    }
    else{
        int ans = dist[N];
        if (ans == INF){
            ans = -2;
        }
        printf("%d\n", ans);
    }
}

int main(){
    scanf("%d%d%d", &N, &ML, &MD);
    for (int i = 0; i < ML; ++i) {
        scanf("%d%d%d", &AL[i], &BL[i], &DL[i]);
    }
    for (int i = 0; i < MD; ++i) {
        scanf("%d%d%d", &AD[i], &BD[i], &DD[i]);
    }

    BuildGraph();

    Bellman_Ford();

    return 0;
}
posted @ 2022-02-16 10:52  dctwan  阅读(78)  评论(0编辑  收藏  举报