POJ 3169 Layout (差分约束系统+SPFA)

原题地址;http://poj.org/problem?id=3169

题意:一个牛舍里有N头牛,有一些牛的关系比较好,他们希望彼此不超过一定的距离。当然也有些牛关系不好,他们希望彼此超过一定的距离。有ML对牛的关系比较好,并给出每对牛的所不超过的距离D;同样,有MD对牛的关系不好,并给出每对牛的所超过的距离D。问是否有满足这样的安排方案满足所有牛的要求。若不存在,输出-1;若存在,但是牛1和牛N之间的距离可以任意大,输出-2;否则,输出牛1和牛N之间的最大距离。

思路:这题算是接触到的第一题差分约束系统的题了.
引用一段大佬关于查分约束系统的理解:

我们用D[i]表示I号奶牛和1号奶牛间的距离。因为在队伍中的顺序必须和编号相同,所以对于任意I号奶牛,1 <= I < N,在距离上应该满足:D[I+1] - D[I] >= 0
对于每个好感的描述(i,j,k),假设i<=j,体现到距离上的要求就是:D[j] - D[I] <= k
对于每个反感的描述(i,j,k),假设i<=j,体现到距离上的要求就是:D[j] - D[I] >= k
这时的模型有一个名称,叫作:差分约束系统。
为了方便起见,我们将每种不等式写成我们约定的形式:
D[I] <= D[I+1]
D[j] <= D[I] + k
D[I] <= D[j] - k
在求顶点间地最短路问题中,我们有这样的不等式:若顶点u到顶点v有边e=uv,且边权为w(e),设d(u),d(v)为源点到顶点u和顶点v的最短路长,则 d(v) <= d(u) + w(e)
这个不等式和前面的条件形式十分相似,这就启发我们用构图用最短路做。
具体步骤是:
作有向图G=(V,E),V={ v1,v2,v3,…,vn},E={e1,e2,e3,…},对于相邻两点i和(i+1),对应的顶点vi+1向vi引一条边,费用为0;对于每组好感描述(ai,bi,di),我们假设有ai < bi,否则ai和bi交换,则顶点vai向vbi引一条边,费用为di;对于每组反感描述(ai,bi,di),我们假设有ai < bi,否则ai和bi交换,则顶点vbi向vai引一条边,费用为-di。

简单来说,假设有一个不等式表示成 u + w >= v ,,那么就可以理解成从顶点 u 连了一条长度为 w 的线段指向了 v .就是要将式子转化为 >= 的形式,

代码如下:

#include <cmath>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
#include <stack>
#include <set>
#include <cctype>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define MOD 1e9+7
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define CLR(x,y) memset((x),y,sizeof(x))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e5 + 5;
int n, ml, md;
struct edge {
    int v, w;
    edge() {}
    edge(int v, int w): v(v), w(w) {}
};
vector<edge>G[maxn];
void add_edge(int u, int v, int w) {
    G[u].push_back(edge(v, w));
}
bool vis[maxn];
int dis[maxn];
int cont[maxn];
bool spfa(int num) {
    CLR(vis, 0);
    CLR(cont, 0);
    CLR(dis, 0x3f);
    queue<int>q;
    dis[num] = 0;
    q.push(1);
    vis[num] = 1;
    cont[num] = 1;
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = 0; i < G[u].size(); i++) {
            int v = G[u][i].v;
            if(dis[v] > dis[u] + G[u][i].w) {
                dis[v] = dis[u] + G[u][i].w;
                if(!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                    if(++cont[v] >= n) return 0; //return 0代表有负环存在
                }
            }
        }
    }
    return 1;
}
int main() {
    scanf("%d%d%d", &n, &ml, &md);
    int u, v, w;
    for(int i = 1; i <= ml; i++) {
        scanf("%d%d%d", &u, &v, &w);
        add_edge(u, v, w);
    }
    for(int i = 1; i <= md; i++) {
        scanf("%d%d%d", &u, &v, &w);
        add_edge(v, u, -w);
    }
    int x = spfa(1);
    if(x == 0) printf("-1\n");
    else if(dis[n] == INF) printf("-2\n");
    else printf("%d\n", dis[n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/81280387