• 题解 P2294 【[HNOI2005]狡猾的商人】


    题目链接

    Solution [HNOI2005]狡猾的商人

    题目大意:给定m个约束条件,每个约束条件表示(sum_{s}^{t} = v),判断图是否有解

    差分约束


    我们现在来分析一下题目:

    (sum_{s}^{t} = v),这个式子不好办,让我们非常难受。那么我们有没有办法将这个复杂的和式变换成其它的形式呢?

    答案是肯定的。对于这种和式,我们可以考虑采用前缀和的方式来将它变换成其它形式

    (sum(i) = sum_0^ia_i) ((a_i)是题目中给定的盈亏值)

    那么那个毒瘤的式子就变成了(sum_{s}^{t} = sum(t) - sum(s - 1) = v)

    有没有觉得这个式子很眼熟,尤其是两数之差是不是让你想到了差分约束?

    可是这个和裸的差分约束还有一点点区别,我们还不能直接套板子,得继续变形

    (sum(t) - sum(s - 1) = v iff egin{cases}sum(t) - sum(s - 1) leq v \ sum(t) - sum(s - 1) geq vend{cases})

    既有(leq)又有(geq)不好办?乘个(-1)嘛。

    (egin{cases}sum(t) - sum(s - 1) leq v \ sum(t) - sum(s - 1) geq vend{cases} iff egin{cases} sum(t) - sum(s-1) leq v \ sum(s - 1) - sum(t) leq -vend{cases})

    然后这就是一个裸的差分约束问题了,直接套板子吧。如果有负环则无解。不过需要注意几点小问题:

    • 有负权,不能用超级无敌狂拽酷炫人类目前最快的单源最短路径算法Dijkstra

    • 图可能不连通(各位应该想到了)。解决办法很简单。原来图里面点的编号是从(0)(n)(0这个点来自于前缀和),所以直接加一个虚拟点(n + 1),从(n + 1)到每个点连一条权值为0的边,跑SPFA即可

    • 别写挂了(这好像是废话Orz)

    最后献上码风丑陋无比的代码

    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int maxn = 1280;
    const int maxm = 10240 << 1;
    struct Edge{
        int from,to,dist;
        Edge() = default;
        Edge(int a,int b,int c):from(a),to(b),dist(c){}
    }Edges[maxm];
    int head[maxn],nxt[maxm],tot,n,m;
    inline void clear_edge(){
        tot = 0;
        memset(head,0,sizeof(head));
        memset(nxt,0,sizeof(nxt));
    }
    inline void addedge(int from,int to,int dist){
        Edges[++tot] = Edge(from,to,dist);
        nxt[tot] = head[from];
        head[from] = tot;
    }//存图没啥好讲的,clear_edge和addedge望文生义吧
    int dist[maxn],cnt[maxn],inq[maxn];
    inline bool spfa(int s){//SPFA,用STL望巨佬们见谅。返回true代表差分约束有解,返回false代表有负环,即差分约束无解
        queue<int> Q;
        memset(cnt,0,sizeof(cnt));
        memset(inq,0,sizeof(inq));
        memset(dist,0x3f,sizeof(dist));
        dist[s] = 0;Q.push(s);inq[s] = true;
        while(!Q.empty()){
            int u = Q.front();Q.pop();
            inq[u] = false;
            for(int i = head[u];i;i = nxt[i]){
                const Edge &e = Edges[i];
                if(dist[e.from] + e.dist < dist[e.to]){
                    dist[e.to] = dist[e.from] + e.dist;
                    if(!inq[e.to]){
                        Q.push(e.to);
                        inq[e.to] = true;
                        if(++cnt[e.to] > n)return false;
                    }
                }
            }
        }
        return true;
    }
    int t;
    inline void solve(){//对一组数据求解
        scanf("%d %d",&n,&m);
        clear_edge();
        for(int i = 1;i <= m;i++){
            int s,t,v;
            scanf("%d %d %d",&s,&t,&v);
            addedge(s - 1,t,v);
            addedge(t,s - 1,-v);//如何连边上文已经提到了
        }
        for(int i = 0;i <= n;i++)
            addedge(n + 1,i,0);
        puts(spfa(n + 1) ? "true" : "false");
    }
    int main(){
        scanf("%d",&t);
        while(t--)
            solve();
        return 0;//收工
    }
    
  • 相关阅读:
    Windows Server 2016 Standard Key激活密钥序列号
    SUSE 15 系统安装过程
    MSSQL 各个发行版本版本号以及Compact 版本号(更新)
    百度地图BaiduMap组件
    uniapp 小程序分包
    css3 nthchild的妙用
    js通过一个数组,删除数据中对应的id
    关于 eltable 无限抖动
    高级前端进阶(五)
    函数柯里化第一版
  • 原文地址:https://www.cnblogs.com/colazcy/p/11514717.html
Copyright © 2020-2023  润新知