zoukankan      html  css  js  c++  java
  • 差分约束系统

    差分约束系统

    简单来说,差分约束是用图论中的最短路解决一些不等式(组)。

    例如:Xi表示序列第i个数,请求出一组满足:X1-X5 <= 1, X1 - X3 <= 3, X2 - X4 <= -2的长度为5的序列

    怎么建图?举个栗子,X1-X5 <= 1, 就从结点5向结点1连一条权为1的边。

    X1-X5 <= 1  ->  X1 <= X5 + 1

    联想到最短路的性质:Dis(i) <= Dis(j) + edge(j, i).(dis_i是源点到i距离)

    两个一一对应一下,把X1看成Dis(1),X5看成Dis(5),1就是这两个结点连出的边权,因此对于每一对Xi - Xj <= Wk, 就从j到i连一条权是Wk的边即可。

    为了图的连通性(为了有个源点)最后再新建一个结点0,连到每个结点,长度是0(即Xi<=X0+0,因为X0不是题目中的结点,所以随意约定它的值不会影响答案)

    从结点0跑单源最短路,满足这一系列不等式的的序列就是最后的Dis(1) 到 Dis(n)。

    图中有负环时无解

    刚刚讲的是Xi - Xj <= Wk的形式,直接j到i连一条权为Wk,如果是 Xi - Xj >= Wk 的话,两边乘以-1,转换成Xj - Xi <= -Wk,所以最后的方法是:i到j连一条权为-Wk的边。

    如果是Xi - Xj = Wk 呢?我们就建两条边,分别是Xi - Xj ≥ Wk ,Xi - Xj ≤ Wk。


    练习

    例1∶Luogu P3275 糖果

    if(x == 1) add(a,b,0),add(b,a,0);
    else if(x == 2) add(a,b,-1);
    else if(x == 3) add(b,a,0);
    else if(x == 4) add(b,a,-1);
    else if(x == 5) add(a,b,0);
    if(x % 2 && a == b){
        printf("-1"); //同一人,但糖数不等,不符合逻辑
        return 0;
    }
    bool SPFA(int x){
        memset(dis,0x3f,sizeof(dis));
        int k;
        s.push(x);
        visit[x] = 1;
        dis[x] = 0;
        while(!s.empty()){
            k = s.front();
            visit[k] = 0;
            s.pop();
            for(int i=head[k];i!=-1;i=edge[i].next)
                if(dis[edge[i].to] > dis[k] + edge[i].w){
                    dis[edge[i].to] = dis[k] + edge[i].w;
                    if(!visit[edge[i].to])s.push(edge[i].to),visit[edge[i].to] = 1,use[edge[i].to]++;
                    if(use[edge[i].to]>n-1)return false;
                }    
        }
        return true;
    }

    例2∶Luogu P1250 种树

    这类题又有些微的不同,Sum(n)是前n个单位种的树的棵树,好比前缀和,所以Sum(k+1)一定大于等于Sum(k)

    1.Sum(k+1) ≤ Sum(k)

    2.Sum(k+1) - Sum(k) ≤ 1

    于是我们这样建边:

    for(int i=1;i<=n;i++) add(i-1,i,1),add(i,i-1,0);

    此时还能为了图的连通将0与每个点相连吗?

    若建边用add(0,i,0),则按照题意就是 Sum(i)  -Sum(0) ≤ 0,这种建边方式不适用于这种类似前缀和的题。

    于是我们只好选择点 L+1以保持图的连通。

    for(int i=1;i<=n;i++) add(n+1,i,0);

    最后,我们要求的是种最少的棵数,然而我们求出的关系是相对的。

    举个栗子,3,4,5,9,2是一种方案,那么1,2,3,7,0是同样成立的。

    于是我们最后要求的最少个数需要减去最小值

    for(int i=0; i<=n; i++) minn = min(minn, dis[i]);
    cout << dis[n] - minn << endl;

     例3:排队

    某些粉丝之间互相喜欢,他们希望互相之间的距离至多为一个定值。但某些粉丝乊间互相厌恶,他们希望互相之间的距离至少为一个定值。现在给定K个互相喜爱的粉丝对以及他们乊间距离的最大值,L个互相厌恶的粉丝对以及他们之间距离的最小值。
    你的任务是计算在满足以上条件的前提下,帮劣YSY计算出编号为1和编号为N的粉丝乊间距离的最大可能值。

    【输入】
    输入文件为 layout.in。
    第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 N,K和L ;
    此后K行,每行包含三个用空格分开的整数A, B和D,其中A,B满足1<=A<=B<=N。表示编号为A和B的粉丝之间的距离至多为D。
    此后L行,每行包含三个用空格分开的整数A,B和D,其中A,B满足1<=A<=B<=N。表示编号为A和B的粉丝之间的距离至少为D。
    【输出】
    输出文件名为 layout.out。
    输出文件仅包含一个整数。如果丌存在任何吅法的排队方式,就输出-1。如果编号1和编号N的粉丝间距离可以任意,就输出-2 。否则输出他们之间的最大可能距离。

    【输入输出样例】
    layout.in

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

    layout.out

    27

    【数据范围】

    对于40%的数据,N<=100;
    对于100%的数据,N<=1000;K, L<=10000;D<=1000000。

    此题和上两题又不一样了,前两题不需要连通,那么我们新加一个点,连接每一个点以保证连通。

    题意中“如果编号1和编号N的粉丝间距离可以任意,就输出-2”,说明图必须连通,所以我们只能选择现有的点。

    for(int i=1;i<=n;i++) add(i,i-1,0);//Sum(i)>=Sum(i-1) 
    if(!SPFA(1)) printf("-1");
    else if(vis[n] == 0)printf("-2");
    else printf("%lld",dis[n]);
  • 相关阅读:
    adb获取不了设备List of devices attached
    Appium常用的API函数
    Clevo P950笔记本加装4G模块
    “CNKI 中国知网 PDF 全文下载”油猴脚本在线安装地址
    使用XTU降低CPU功耗,自动执行不失效
    Clevo P950系列拆机
    Win10的WSL很好用呀
    ubuntu下opencv使用cvNamedWindow()和cvShowImage()出错的解决方法
    2017年研究生数学建模D题(前景目标检测)相关论文与实验结果
    [翻译]怎么阅读一篇论文
  • 原文地址:https://www.cnblogs.com/Cindy-Chan/p/11254628.html
Copyright © 2011-2022 走看看