差分约束


差分约束算法

P5960 【模板】差分约束算法

约束条件:xixjck

可以变成:xick+xj

我们将所有条件变成只取等号的形式:xi=ck+xj

我们将所有的条件之间连上边,然后跑最短路,这样可以保证所有的约束条件一定都符合要求。

将所有点和源点连边(防止有点在求最短路时落掉)。

但是,会出现负环的情况,即没有解的情况,我们可以考虑如果一组有解,那么一个点最多被使用 n1 次(自己不连自己),加上源点,就是 n 次,而负环会一直遍历下去,所以我们直接判断点遍历的次数有没有超过 n,即可。若超过直接返回无解。

题解

cpp
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
long long r_r(){ 
  long long k=0,f=1;
  char c=getchar();
  while(!isdigit(c)){
    if(c=='-')f=-1;
    c=getchar();
  }
  while(isdigit(c)){
    k=(k<<1)+(k<<3)+(c^48);
    c=getchar(); 
  }
  return k*f;
}
const int o_o=1e4+10;
struct po{
  int v,l,n_t;
}p_p[o_o];
int h_d[o_o],t_t[o_o],v_v[o_o],b_b[o_o],x_p,n,m;
void a_d(int u,int v,int w){
  p_p[++x_p].v=v;
  p_p[x_p].l=w;
  p_p[x_p].n_t=h_d[u];
  h_d[u]=x_p;
}
queue<int> q;
bool s_f(int s){
  memset(v_v,63,sizeof(v_v));//初始化各个节点价值 
  v_v[s]=0,b_b[s]=1;//初始化源点信息 
  q.push(s);//加入源点 
  while(!q.empty()){
    int k=q.front(); 
    q.pop();
    b_b[k]=0;//初始化节点价值 
    for(int i=h_d[k];i;i=p_p[i].n_t){
      int v=p_p[i].v;
      if(v_v[v]>v_v[k]+p_p[i].l){//找到价值更小的解法 
        v_v[v]=v_v[k]+p_p[i].l;//更新 
        if(!b_b[v]){//目标节点还未遍历过 
          b_b[v]=1;//标记 
          t_t[v]++;//更新使用节点次数 
          if(t_t[v]>n)return 0;//出现负环 
          q.push(v);//加入遍历队伍 
        }
      }
    }
  }
  return 1;
}
int main(){
  n=r_r(),m=r_r();
  for(int i=1;i<=n;i++)a_d(0,i,0);//所有未知量和源点连边 
  for(int i=1;i<=m;i++){
    int v=r_r(),u=r_r(),w=r_r();
    a_d(u,v,w);//存边 
  }
  if(!s_f(0))puts("NO");//不存在路径 
  else for(int i=1;i<=n;i++)printf("%d ",v_v[i]);//输出结果 
  return 0;
}

糖果

P3275 【SCOI2011】糖果

我们将边权定义为当前点到下一个点如果合法的话,至少比当前点的糖果数多加几个糖果,所没回我们找到的都是至少合法。

那么我们遍历的就不能是最短路,以为如果是最短路更新边的话,一定会出现不合法情况。

本题连最长路,如果出现正环,说明不合法。

  • 如果 X=1, 表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多;

(A,B)=0,(B,A)=0 连边,保证 A,B 值相等。

  • 如果 X=2, 表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果;

(A,B)=1 连边,保证表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果 1 个。

  • 如果 X=3, 表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果;

(B,A)=0 连边,保证表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果。

  • 如果 X=4, 表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果;

(B,A)=1 连边,保证表示第 B 个小朋友分到的糖果必须少于第 A 个小朋友分到的糖果 1 个。

  • 如果 X=5, 表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果;

(A,B)=0 连边,保证表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果。

代码

cpp
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
long long r_r(){ 
  long long k=0,f=1;
  char c=getchar();
  while(!isdigit(c)){
    if(c=='-')f=-1;
    c=getchar();
  }
  while(isdigit(c)){
    k=(k<<1)+(k<<3)+(c^48);
    c=getchar(); 
  }
  return k*f;
}
const int o_o=1e6+10;
struct po{
  int v;
  int n_t;
  int l;
}p_p[o_o];
int n,k,h_d[o_o],x_p,v_v[o_o],t_t[o_o];
bool b_b[o_o];
queue<int>q;
void a_d(int u,int v,int l){//加边 
  p_p[++x_p].v=v;
  p_p[x_p].n_t=h_d[u];
  p_p[x_p].l=l;
  h_d[u]=x_p;
}
int main(){
    n=r_r(),k=r_r();
    while(k--){
        int op=r_r(),u=r_r(),v=r_r();
        if(op==1)a_d(u,v,0),a_d(v,u,0);//建来回边保证值得相同 
        else if(op==2){
            if(u==v){
        puts("-1");//自己不能小于自己 
        return 0;
      }
            a_d(u,v,1);//A 连向 B,且 B 一定比 A 至少大 1  
        }
        else if(op==3)a_d(v,u,0);//A 不小于 B 
        else if(op==4){
            if(v==u){
              puts("-1");//自己不能小于自己 
              return 0;
      }
            a_d(v,u,1);//B 连向 A,且 A 一定比 B 至少大 1 
        }
        else if(op==5)a_d(u,v,0);//B 不小于 A 
    }
    for(int i=n;i>=1;i--)a_d(0,i,1);//与源点建边 
    b_b[0]=1,q.push(0);//从源点开始跑 
    while(!q.empty()){
        int k=q.front();
    q.pop();
    b_b[k]=0;
        if(t_t[k]==n-1){//存在正环 
      puts("-1");
      return 0;
    }
        t_t[k]++;//使用次数累计 
        for(int i=h_d[k];i;i=p_p[i].n_t){
          int v=p_p[i].v;
          if(v_v[v]<v_v[k]+p_p[i].l){//更长 
                v_v[v]=v_v[k]+p_p[i].l;//更新结果 
                if(!b_b[v]){//没使用过 
                  b_b[v]=1;//标记 
          q.push(v);//遍历 
        }
            }
    }
    }
    long long a_s=0;
    for(int i=1;i<=n;i++)a_s+=v_v[i];//统计答案 
    printf("%lld ",a_s);//输出结果 
    return 0;
}

文章作者: 王大神——A001
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 王大神——A001 !