POJ-3159 Candies(差分约束)

差分约束系统

最近新学的,是在学最短路时看到的。可以转化为最短路来求解,刚开始一点也不懂,那玩意儿怎么这么神奇!
之后两天,一直搞这个东西,这才稍微理解了一点。

先从定义出发。
给你一大堆不等式,然后叫你求给定不等式的最值。(比较难理解,必须搞一个很牛逼的例子)
例如:
x2x12
x3x15
x3x24
x4x21
x4x33
x5x36
x5x47
现在让求在满足上面每个不等式的条件下, x5x1 的最大值?

  1. ② + ⑥ x5x111
  2. ② + ⑤ + ⑦ x5x115
  3. ① + ④ + ⑦ x5x110
  4. ① + ③ + ⑥ x5x112
  5. ① + ③ + ⑤ + ⑦ x5x116

所以,要满足上面的所有条件,只能 x5x110 。(稍微想想应该很好理解)那么,所求的最大值就是 10 。(这个应该很简单)

全是两个未知数的差小于等于一个常数(大于等于其实是一样的,移项或者乘个 1 就变成了小于等于),这样的不等式组就叫 差分约束系统

对于这个不等式组解的情况,要么无解,要么有无数多解。因为,一旦有了一组解,加上任意一个常数k,各个不等式依旧成立。

接下来就是怎么将这个问题转化为最短路求给定不等式的最值了。

最短路的几个算法就不再详细说了。
通常求最短路时都会有一个 dis[] 数组, dis[i]i 节点到源点的最短路径是多长。
那么,对于任意两个相邻节点 uv 来说, dis[u]+cost[u][v]dis[v] ,这个不等式是一定满足的。(想想Dijkstra 是怎么通过一个节点u来松弛相邻节点v的?)
对不等式移项, dis[v]dis[u]cost[u][v] 。在没求出最短路之前, dis[v]dis[u] 就是未知数,而 cost[u][v] 正好就是常数。和上面的 差分约束系统 惊人的相似!
求最短路时的每步松弛操作都是满足不等式的,所以,解 差分约束系统 就可以类比为求最短路,由此,最短路算法就变成了 解差分约束系统的利器。

对于差分例子的不等式组中的每一个不等式 XY<=C ,建立一条由 Y 指向 X 且边权为 C 的有向边,以 x1 为源点,跑最短路,最后所得出的 dis[x5] 就是 x5x1 的最大值。

对于上面的不等式组,建图如下:
这里写图片描述
未知数 xii 号节点。
上面 5x5x1 的不等式对应路径如下:

1>3>5dis[5]=11
1>3>4>5dis[5]=15
1>2>4>5dis[5]=10
1>2>3>5dis[5]=12
1>2>3>4>5dis[5]=16

最小值 10 对应着所求答案的最大值。

差分约束系统如何用图论方法解决就先说这么多,需要慢慢领悟。

小总结:
形如 XYC 的不等式组,一般会让求 XtXs 的最大值(即求 XtXsTT 值,求最小值完全无意义啊!)。
初始化 dis[i]=INF , 然后对于每个 XiYiCi ,建立一条 Yi 指向 Xi 边权为 Ci 的边,以 Xs 为源点,求最短路,最后得出的 dis[Xt] 即为所求的 最大值 ( T )。若dis[Xt]==INF ,及无法到达,或存在负权回路,则不等式组无解。

形如 XYC 的不等式组,一般会让求 XtXs 的最小值(即求 XtXsT 的那个 T 值,求最大也完全无意义!)
初始化 dis[i]=INF , 然后对于每个 XiYiCi ,建立一条由 Yi 指向 Xi 边权为 Ci 的边,以 Xs 为源点,求最长路(一般求最长路都是把边权乘 1 ,然后求最短路)。最后得出的 dis[Xt] 即为所求的最小值( T )。若dis[Xt]==INF,及无法到达,或存在正权回路,则不等式组无解。
注意 <> 也要转化为 ,例如 XY<C ,可转化为 XYC1




下面通过例题来加深印象:
这一阵做POJ上的 差分约束系统,做的心累啊!!!



题目链接

POJ-3159

题目大意

n 个孩子分糖果,给出 m 个要求 ABC ,要求孩子 B 的糖果最多比孩子 A 的多 C 。问在满足所有要求的前提下,孩子 n 的糖果与孩子 1 的最大差值是多少?
题目保证所求差值是有限的,即保证有解。

数据范围

n30000m150000

解题思路

令孩子 i 的糖果数为 F[i],对于每个给出的关系都可得 F[B]F[A]C
典型的 差分约束系统,对于每个关系,建立 A>B 边权为 C 的有向边。以 1 号点为源点,跑最短路,得出的 dis[n] 即为所求最大差值。


外话
这可能是道假题!一般求最短路,用SPFA+queue就可以了,然而,TLE到死!queue的原因?不是,改成手写队列之后RE到死!什么SPFA?!都是假的,,,改成Dijkstra,轻松过掉!
去搜了一把,什么?!有大佬用stack写SPFA!还有这种操作?!NB勒!


AC代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 200005;

int n, m;
int all;
int pre[2 * MaxN + 5], last[MaxN + 5], other[2 * MaxN + 5];
int cost[2 * MaxN + 5];
int dis[MaxN + 5];

struct Node {
    int id, d;
    Node () {}
    Node (int a, int b) : id(a), d(b) {}
    bool friend operator < (Node x, Node y) {
        return x.d > y.d;
    }
};

void build(int x, int y, int w) {
    pre[++all] = last[x];
    last[x] = all;
    other[all] = y;
    cost[all] = w;
}

void Dijkstra(int s) {
    for(int i = 1; i <= n; i++) dis[i] = inf;
    dis[s] = 0;
    priority_queue <Node> pq;
    pq.push(Node(s, 0));
    while(!pq.empty()) {
        Node now = pq.top();
        pq.pop();
        int ed = last[now.id];
        while(ed != -1) {
            int dr = other[ed];
            if(dis[now.id] + cost[ed] < dis[dr]) {
                dis[dr] = dis[now.id] + cost[ed];
                pq.push(Node(dr, dis[dr]));
            }
            ed = pre[ed];
        }
    }
}

int main() 
{
    while(scanf("%d %d", &n, &m) != EOF)
    {
        all = -1; memset(last, -1, sizeof(last));
        for(int i = 1; i <= m; i++) {
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            build(u, v, w);  //建立u -> v, 边权为w的有向边
        }
        Dijkstra(1);
        printf("%d\n", dis[n]);
    }
    return 0;
}


这里还有一道:POJ-3169

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值