首页 > 其他分享 >【学习笔记】差分约束学习笔记

【学习笔记】差分约束学习笔记

时间:2023-03-20 17:44:23浏览次数:49  
标签:le int 差分 学习 edge maxn 笔记 Rightarrow dis

简述

差分约束系统,是由 \(n\) 个元素和 \(m\) 个约束条件构成的,其中每个约束条件形如 \(x_i-x_j\le y_k\)。

求解

移项得到 \(x_i\le x_j+y_k\),这样三角形不等式的形式类似最短路中的松弛,即由 \(j\) 向 \(i\) 连 \(y_k\) 权值的边,最短路即满足约束条件。

这样便需要一个源点,原图不一定联通,因此建出超级源点 \(n+1\),且规定 \(x_i\le 0\),即 \(x_i \le x_{n+1}+0\),由 \(n+1\) 向 \(i\) 连 \(0\) 权值的边。

这样的负权图最短路可以使用 已死算法 SPFA 来求出。

点击查看代码
int n,m;
struct Graph{
    struct edge{
        int v,w;
        edge()=default;
        edge(int v_,int w_):v(v_),w(w_){}
    };
    vector<edge> E[maxn];
    inline void add_edge(int u,int v,int w){
        E[u].push_back(edge(v,w));
    }
    int dis[maxn],cnt[maxn];
    bool vis[maxn];
    inline void SPFA(){
        queue<int> q;
        memset(dis,0x3f,sizeof(dis));
        dis[n+1]=0,cnt[n+1]=0,vis[n+1]=1;
        q.push(n+1);
        while(!q.empty()){
            int u=q.front();
            vis[u]=0;
            q.pop();
            for(edge e:E[u]){
                int v=e.v,w=e.w;
                if(dis[u]+w<=dis[v]){
                    dis[v]=dis[u]+w,cnt[v]=cnt[u]+1;
                    if(cnt[u]>n) return printf("NO\n"),void();
                    if(!vis[v]){
                        vis[v]=1;
                        q.push(v);
                    }
                }
            }
        }
        for(int i=1;i<=n;++i) printf("%d ",dis[i]);
        printf("\n");
    }
}G;

int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i) G.add_edge(n+1,i,0);
    for(int i=1;i<=m;++i){
        int u=read(),v=read(),w=read();
        G.add_edge(v,u,w);
    }
    G.SPFA();
    return 0;
}

对约束条件的调整

一些简单的变形:

  • \(x_i-x_j\le k\Rightarrow w(j,i)=k\)

  • \(x_i-x_j\ge k\Rightarrow x_j-x_i\le -k\Rightarrow w(i,j)=-k\)

  • \(x_i=x_j\Rightarrow x_i-x_j\le 0\land x_j-x_i\le 0\Rightarrow w(i,j)=w(j,i)=0\)

  • \(x_i-x_j< k\Rightarrow x_i-x_j\le k-1\Rightarrow w(j,i)=k-1\)

  • \(x_i-x_j> k\Rightarrow x_i-x_j\ge k+1\Rightarrow w(i,j)=-k-1\)

对解的特殊限制

事实上,当 \((x_1,x_2,\cdots,x_n)\) 为一组解时,\((x_1+k,x_2+k,\cdots,x_n+k)\) 同样为一组解。因此不等式组要么无解要么有无数组解,无解情况即为没有最短路也就是存在负环。

而在初始的设计中,\(x_i\le 0\),当需要求出特定范围的解时,可以通过限定 \(dis_{n+1}\) 的初始值或 \(w(n+1,i)\) 的值来规定。假设将值修改为 \(c\),则本质上是有 \(x_i\le c\) 的限定。

值得注意的是,最短路求出的结果一定是所有解中最大的。证明考虑最短路树,树边一定满足 \(x_i=x_j+w(j,i)\) 而一旦增加 \(x_j\) 的值就会使答案不合法。

如果要求出满足 \(x_i\ge k\) 的所有解中和最小的值,该如何处理?

首先可以通过变号得到 \(-x_i\le -1\),这一点可以对 \(dis_{n+1}\) 进行修改。这样所有的 \(x\) 值都已经取反,则 \(x_i=x_j+w(j,i)\) 变作 \(-x_j=-x_i+w(j,i)\),即 有向边方向改变,边权不变,此时最短路得到的最大解与要求最小解互为相反数。

例题

洛谷-P3275 [SCOI2011] 糖果

建模比较容易,关键是 SPFA 被卡了。

发现边权只有 \(0\) 或 \(-1\),那么一个 SCC 中存在边权 \(-1\) 即无解,这样每个 SCC 都一定相等,最短路用拓扑排序求出。

点击查看代码
int n,m;
ll ans;
struct Grpah{
    struct edge{
        int v,w;
        edge()=default;
        edge(int v_,int w_):v(v_),w(w_){}
    };
    vector<edge> E[maxn];
    inline void add_edge(int u,int v,int w){
        E[u].push_back(edge(v,w));
    }
    int dfn[maxn],low[maxn],dfncnt;
    int st[maxn],top;
    int scc[maxn],scccnt,siz[maxn];
    inline void Tarjan(int u){
        dfn[u]=++dfncnt,low[u]=dfn[u];
        st[++top]=u;
        for(edge e:E[u]){
            int v=e.v;
            if(!dfn[v]){
                Tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            else if(!scc[v]) low[u]=min(low[u],dfn[v]);
        }
        if(dfn[u]==low[u]){
            ++scccnt;
            while(st[top]!=u){
                scc[st[top]]=scccnt;
                ++siz[scccnt];
                --top;
            }
            scc[u]=scccnt;
            ++siz[scccnt];
            --top;
        }
    }
    vector<edge> dagE[maxn];
    int deg[maxn];
    int dis[maxn];
    inline void solve(){
        for(int i=1;i<=n;++i){
            if(!dfn[i]) Tarjan(i);
        }
        for(int u=1;u<=n;++u){
            for(edge e:E[u]){
                int v=e.v;
                if(scc[u]==scc[v]){
                    if(e.w==-1) return printf("-1\n"),void();
                }
                else{
                    dagE[scc[u]].push_back(edge(scc[v],e.w));
                    ++deg[scc[v]];
                }
            }
        }
        for(int i=1;i<=scccnt;++i){
            dagE[scccnt+1].push_back(edge(i,-1));
            ++deg[i];
        }
        queue<int> q;
        memset(dis,0x3f,sizeof(dis));
        dis[scccnt+1]=0;
        q.push(scccnt+1);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(edge e:dagE[u]){
                int v=e.v,w=e.w;
                if(dis[u]+w<dis[v]) dis[v]=dis[u]+w;
                --deg[v];
                if(!deg[v]) q.push(v);
            }
        }
        for(int i=1;i<=scccnt;++i){
            ans-=1ll*siz[i]*dis[i];
        }
        printf("%lld\n",ans);
    }
}G;

int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i) G.add_edge(n+1,i,-1);
    for(int i=1;i<=m;++i){
        int opt=read(),u=read(),v=read();
        if(opt==1) G.add_edge(u,v,0),G.add_edge(v,u,0);
        else if(opt==2) G.add_edge(u,v,-1);
        else if(opt==3) G.add_edge(v,u,0);
        else if(opt==4) G.add_edge(v,u,-1);
        else G.add_edge(u,v,0);
    }
    G.solve();
    return 0;
}

参考资料

标签:le,int,差分,学习,edge,maxn,笔记,Rightarrow,dis
From: https://www.cnblogs.com/SoyTony/p/Learning_Notes_about_Difference_Constraints.html

相关文章

  • 构建之法阅读笔记01
    第一章概论在这一章中,作者为我们介绍了一些关于软件工程的基本知识。①软件=程序+软件工程:正是因为对软件开发活动(构建管理、源代码管理、软件设计、软件测试、项目管理......
  • 重链剖分学习笔记+做题记录
    一、理论知识首先放一张图(明显是OI-Wiki的):\(u\)的子节点\(p_1,p_2,\dots,p_k\)中子树最大的节点叫做重儿子,如有多个,任取其一,记作\(son_u\)。\(u\)除掉\(so......
  • C 栈模板及笔记
    文章目录​​Intro​​​​1.熟练掌握栈的定义、特性和栈的抽象数据类型,​​​​定义​​​​是限定仅在队尾进行插入或删除操作的先行比爱​​​​特性​​​​表尾端......
  • Fortran读书笔记(3)
    本篇文章为本人读气象出版社的fortran程序设计,若有侵权,请私信,本人立即删除数组的定义数组举例:integera(-5,5),b(20)character*8d(50)dimensiona(2,3)integera......
  • 《深入理解计算机系统》第四章学习笔记 处理器体系结构
    一个处理器支持的指令和指令的字节级编码称为它的指令集体系结构。不同的处理器“家族”,例如IntelIA32和x86-64、IBM/FreescalePower和ARM处理器家族,都有不同的ISA。一个......
  • TypeScript 学习笔记 — 类型兼容 (十)
    目录一.基本数据类型的兼容性二.接口兼容性三.函数的兼容性四.类的兼容性类的私有成员和受保护成员五.泛型的兼容性六.枚举的兼容性标称类型简短介绍TS是结构类型系统(str......
  • 手工测试如何转型自动化测试,我整理的3000字超全学习指南
    行业在发展,企业要求越来越高,最近经常有粉丝在后台问我:手工测试想转型自动化,请问应该怎么入手?有没有好的教程推荐?三言两语说不明白,我就根据自己的职业经历聊一聊如何在工......
  • C 链表模板及笔记
    文章目录​​Intro​​​​理解线性表的基本概念(逻辑特性、术语及物理存储)​​​​基本概念​​​​n(n>=0)个数据特性相同的元素构成的有限序列称为线性表​​​​逻辑特......
  • Nginx 学习(二)
    Nginx简介Nginx是开源、高性能、高可靠的Web和反向代理服务器,而且支持热部署,几乎可以做到7*24小时不间断运行,即使运行几个月也不需要重新启动,还能在不间断服务......
  • Asp.Net MVC学习笔记2
       Areas区域。随着项目的不断扩大,Controllers控制器也在不断变多,这样即时使用文件夹分隔也不易于项目维护,和阅读。Asp.NetMVC提供了Areas的功能可以把项目中的没一......