题解 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;//收工
}
原文地址:https://www.cnblogs.com/colazcy/p/11514717.html