• LUOGU P2294 [HNOI2005]狡猾的商人(差分约束)


    [传送门] (https://www.luogu.org/problemnew/show/P2294)

    解题思路

      差分约束。先总结一下差分约束,差分约束就是解决一堆不等式混在一起,左边是差的形式,右边是常量,然后要求差最小值最大值或判无解的算法。首先对于下面几个不等式来说:

    [X_0-X_1<=5\ X_2-X_3>=2\ X_1-X_3<=1\ X_0-X_2<=3 ]

      现在我们要求(X_1-X_3)的最大值,我们观察第一个式子(其实哪个都行),第一个式子可以变(X_0<=X_1+5),这个形式不正是最长路的松弛操作吗。所以我们可以化作图论的模型,对于(X_2-X_3>=2)这样的式子,我们只需要在两边同时乘一个(-1),也就变成了(X_2-X_3<=-2)了。这样的话我们建图,(X_1)(X_3)的最长路即为答案。最小值也同理,我们把所有式子化成(>=)的形式就行了。

      然后有几个建图的技巧。对于(X_a-X_b=c)的形式,我们可以把它拆成两个式子:$ X_a-X_b<=c $ 和 $X_a-X_b>=c $,对于 $ X_a-X_b<c $,我们可以把它变成 $ X_a-X_b<=c-1$的形式。然后注意有的题让判无解,当求最短路的时候出现了负环时就一定无解,因为会不断松弛。因为有负边的存在,所以用 (spfa)

      说一下这道题,这道题比较巧妙。首先给的一堆([l,r])的的收入,我们可以看做一个前缀和的形式。即(sum[r]-sum[l-1]=k)(k)是一个常数,这就变成上面差分约束的形式了,然后我们判断是否是假的,只需要建好图用(spfa)判负环。

    代码

    (Atom)写的,缩进有点奇怪。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<queue>
    
    using namespace std;
    const int MAXN = 105;
    const int MAXM = 1005;
    
    inline int rd(){
      int x=0,f=1;char ch=getchar();
      while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
      while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
      return f?x:-x;
    }
    
    int T,n,m,head[MAXN],cnt,num[MAXN],dis[MAXN];
    int to[MAXM<<1],nxt[MAXM<<1],val[MAXM<<1];
    bool vis[MAXN],flag;
    queue<int> Q;
    
    inline void add(int bg,int ed,int w){
      to[++cnt]=ed,nxt[cnt]=head[bg],head[bg]=cnt,val[cnt]=w;
    }
    
    bool spfa(int S){
      memset(vis,false,sizeof(vis));
      memset(dis,0x3f,sizeof(dis));
      memset(num,0,sizeof(num));
      Q.push(S);dis[S]=0;vis[S]=1;num[S]=1;
      int x,u;
      while(!Q.empty()){
        x=Q.front();Q.pop();
        for(register int i=head[x];i;i=nxt[i]){
          u=to[i];
          if(dis[x]+val[i]<dis[u]){
            dis[u]=dis[x]+val[i];
            num[u]=max(num[u],num[x]+1);
            if(num[u]>n) return false;
            if(!vis[u]) {vis[u]=1;Q.push(u);}
          }
        }
        vis[x]=false;
      }
      return true;
    }
    
    inline void init(){
      memset(head,0,sizeof(head));
      while(Q.size()) Q.pop();
      cnt=0;flag=false;
    }
    
    int main(){
      T=rd();
      while(T--){
        init();
        n=rd();m=rd();int x,y,z;
        for(int i=1;i<=m;i++){
          x=rd(),y=rd(),z=rd();
          add(y,x-1,z),add(x-1,y,-z);
        }
        for(int i=0;i<=n;i++)
        	if(!spfa(i)) {flag=true;break;}
        puts(flag?"false":"true");
      }
      return 0;   
    }
    
  • 相关阅读:
    动态规划-石子问题
    动态规划-最长不下降子序列
    STL 二分查找
    动态规划-最长公共子序列与最长公共子串
    动态规划-背包问题
    高精度运算模板学习
    二叉树 | 根据前序、后序生成中序
    03.动画
    02.绘制函数
    01.hello world
  • 原文地址:https://www.cnblogs.com/sdfzsyq/p/9826857.html
Copyright © 2020-2023  润新知