POJ-3169 Layout (差分约束)

来源:互联网 发布:淘宝旺旺卖家版2016 编辑:程序博客网 时间:2024/06/16 05:41

题目链接

POJ-3169

题目大意

n 个点在数轴上排成一排,并要求 pos[i+1]pos[i].
先给出 ML 个关系A,B,C(A<B)要求 A 点和 B 点之间的距离不能超过 C
再给出 MD 个关系 E,F,G(E<F) 要求 E 点到 F 点的距离最少是 G
问是否存在这样一个序列满足所有要求?若不存在则输出 1,否则,若 n 号点到 1 号点的距离为 INF,输出 2,不然输出 n 号点到 1 号点的距离。

数据范围

2n10001ML100001MD10000
1A<Bn1E<Fn,1C,G106

解题思路

典型的差分约束。
D[i]i 号点所在的位置。
那么,对于任意的 i(1i<n)都有 D[i+1]D[i]
对于任一 ML 关系,则有 D[A]+CD[B]
对于任一 MD 关系,则有D[E]+GD[F]

对上面三个式子进行变换,得:
D[i]D[i+1]0
D[B]D[A]C
D[E]D[F]G

那么,对于任意的 i(1i<n),从 (i+1)i 建立一条边权为 0 的有向边;但在代码中却没有建出,注意代码中建树前有一个swap(),将编号较小的点放在前面,以此来保证这个条件。
对于任一的 ML 关系,从 AB 建立一条边权为 C 的有向边;
对于任一的 MD 关系,从 FE 建立一条边权为 G的有向边。
1 号点为源点,跑最短路,若不存在负权回路,最后得出的 dis[n] 即是问题的答案。(dis[n]==INF,2);若存在负权回路,则输出 1
由于这道题可能存在负权回路,就不能用Dijkstra,可以用SPFA判负权回路。
对于SPFA判断负权回路,记一个 cnt[] 数组,cnt[i] 表示 i 号节点进入队列的次数,若某一节点入队超过 n 次,则存在负权回路!(详见代码)
这与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;}
原创粉丝点击