图论:最短路-差分约束

如果一个系统由n个变量和m个约束条件组成,形成m个形如ai-aj≤k的不等式(i,j∈[1,n],k为常数),则称其为差分约束系统

举一个例子,给定n个变量和m个不等式,每个不等式的形式为x[i]-x[j]<=a[k](0<=i,j<n,0<=k<m,a[k]已知)求x[i]-x[j]的最大值

为了解决这一类问题,需要先将所有的约束条件统一一下格式:

如果需要求的是两个变量差的最大值,那么需要将所有不等式转变成"<=",建图后求最短路

对于每个不等式x[i]-x[j]<=a[k],对结点j和i建立一条j->i的有向边,边权为a[k],求x[n-1]-x[0]的最大值就是求0到n-1的最短路

如果需要求的是两个变量差的最小值,那么需要将所有不等式转化成">=",建图后求最长路

下面一BZOJ2330为例,这是一个可以转化为差分约束问题的求最小值的一个例子

const int maxn=100005;
int n,k,cnt;
long long ans;
int q[maxn],head[maxn],d[maxn],cir[maxn];
bool inq[maxn];
struct data{int to,next,v;}e[4*maxn];
void insert(int u,int v,int w)
{e[++cnt].to=v;e[cnt].next=head[u];e[cnt].v=w;head[u]=cnt;}

n个小朋友,k个限制条件,cnt是邻接表边的数量

ans是最终的答案,q,head,d是最短路相关的数组(此处是求的最长路),cir是用来统计每个点的入队次数的,用来判环

bool spfa()
{
    int h=0,t=1,now;
    q[t]=0,inq[0]=1;cir[0]=1;
    while(h!=t)
    {
        h=h%maxn+1;
        now=q[h];
        for(int i=head[now];i;i=e[i].next)
        {
            if(d[now]+e[i].v>d[e[i].to])
            {
                d[e[i].to]=d[now]+e[i].v;  //最长路
                if(++cir[e[i].to]>=n) return 0;
                if(!inq[e[i].to])
                {
                    inq[e[i].to]=1;
                    t=t%maxn+1;q[t]=e[i].to;
                } 
            }
        }
        inq[now]=0;
    }
    return 1;
}

这里的spfa是可以判环的,并且求的是最长路,这点需要注意

然后,针对每一个限制条件:

            case 1:insert(a,b,0);insert(b,a,0);break;//b>=a a>=b -> a=b
            case 2:if(a==b){printf("-1");return 0;}
                insert(a,b,1);break;//b>=a+1 -> b>a
            case 3:insert(b,a,0);break;//a>=b
            case 4:if(a==b){printf("-1");return 0;}
                insert(b,a,1);break;//a>=b+1 -> a>b
            case 5:insert(a,b,0);break;

因为题目要求的是,所有人分得糖果的和的最小值,那么需要假设一个虚拟节点0,然后以0作为源点计算从源点到所有点的最长路

之后我统计d数组就可以得到0到每一个节点的距离,这个距离值可以转化成差分约束条件:

求A-B的最小值就是求B到A的最长路,那么本题中,计算的任一点x到0的最长路dx也就是x-0>=dx,那么就有x>=dx,dx就因此成了x的下界

遇到最值问题的时候,找到合适的源点,分清是求最大值还是最小值,将差分约束条件进行合理地转化是解决问题的关键

这里给出利用差分约束系统求最大值并结合最“短”路模型时的几种约束条件的变式:

X[n-1]-X[0]>=T ,可以进行移项转化为: X[0]-X[n-1]<=-T。
X[n-1]-X[0]<T, 可以转化为X[n-1]-X[0]<=T-1。
X[n-1]-X[0]=T,可以转化为X[n-1]-X[0]<=T&&X[n-1]-X[0]>=T,再利用第一种方式进行转化即可

下面给出题目完整的实现:

 1 #include<cstdio> 
 2 const int maxn=100005;
 3 int n,k,cnt;
 4 long long ans;
 5 int q[maxn],head[maxn],d[maxn],cir[maxn];
 6 bool inq[maxn];
 7 struct data{int to,next,v;}e[4*maxn];
 8 void insert(int u,int v,int w)
 9 {e[++cnt].to=v;e[cnt].next=head[u];e[cnt].v=w;head[u]=cnt;}
10 bool spfa()
11 {
12     int h=0,t=1,now;
13     q[t]=0,inq[0]=1;cir[0]=1;
14     while(h!=t)
15     {
16         h=h%maxn+1;
17         now=q[h];
18         for(int i=head[now];i;i=e[i].next)
19         {
20             if(d[now]+e[i].v>d[e[i].to])
21             {
22                 d[e[i].to]=d[now]+e[i].v;  //最长路
23                 if(++cir[e[i].to]>=n) return 0;
24                 if(!inq[e[i].to])
25                 {
26                     inq[e[i].to]=1;
27                     t=t%maxn+1;q[t]=e[i].to;
28                 } 
29             }
30         }
31         inq[now]=0;
32     }
33     return 1;
34 }
35 int main()
36 {
37     scanf("%d%d",&n,&k);
38     int x,a,b;
39     while(k--)
40     {
41         scanf("%d%d%d",&x,&a,&b);
42         switch(x)
43         {
44             case 1:insert(a,b,0);insert(b,a,0);break;//b>=a a>=b -> a=b
45             case 2:if(a==b){printf("-1");return 0;}
46                 insert(a,b,1);break;//b>=a+1 -> b>a
47             case 3:insert(b,a,0);break;//a>=b
48             case 4:if(a==b){printf("-1");return 0;}
49                 insert(b,a,1);break;//a>=b+1 -> a>b
50             case 5:insert(a,b,0);break;
51         }
52     }
53     for(int i=n;i>=1;i--) insert(0,i,1);//i>=0+1 -> i>0
54     if(!spfa()) {printf("-1");return 0;}  //有环
55     for(int i=1;i<=n;i++) ans+=d[i];  //每一个点与0的距离就是每一个人分到的糖果数 
56     printf("%lld",ans); 
57     return 0;
58 }

转载于:https://www.cnblogs.com/aininot260/p/9398280.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值