差分约束

类型:给出一些形如$a-b<=k$的不等式(或$a-b>=k$或$a-b<k$或$a-b>k$等),问是否有解或求差的极值。
例子:$b-a<=k1$,$c-b<=k2$,$c-a<=k3$。将$a$,$b$,$c$转换为节点;$k1$,$k2$,$k3$转换为边权;减数指向被减数,形成一个有向图:

由题可得$(b-a)\ +\ (c-b)\ <=\ k1+k2$,$c-a<=k1+k2$。比较$k1+k2$与$k3$,其中较小者就是$c-a$的最大值。
由此我们可以得知求差的最大值,即上限被约束,此时我们拿最小的限制,也就是跑最短路;反之,求差的最小值,下限被约束,我们跑最长路。
跑最短路时:$d[v]<=d[u]+w$
跑最长路时:$d[v]>=d[u]+w$
路径中可能会存在负边,我们用SPFA跑。

例题1:http://qscoj.cn/#/problem/show/1961

题解:设sum[x]为前x个咕咕中至少需要赶走的咕咕数,则$sum[b]-sum[a-1]>=c$表示$[a,b]$区间至少赶走c只。题目中选择的是最少,我们需要跑最长路,因存在负边,所以以SPFA进行操作。$d[v]>=d[u]+w$,前面我们可以推出第一个式子$sum[b]>=sum[a-1]+c$,但是如果只连这些边,整张图连通不起来。我们发现$i$和$i+1$存在关系$0<=sum[i+1]-sum[i]<=1$,这个其实就是表示$i+1$那个位置赶与不赶。推出第二个和第三个式子:$sum[i]>=sum[i+1]-1,sum[i+1]>=sum[i]+0$。

由以上式子得到边:$a-1点\    b点\     距离c$

                                $i+1点\      i点\     距离-1$

           $i点\        i+1点\   距离0$

 1 #include <queue>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 const int INF=0x3f3f3f3f;
 9 const int N=2e5+10;
10 int k,n,cnt;
11 int st=INF,en=-INF;
12 bool vis[N];
13 int head[2*N],d[N];
14 
15 struct Edge{
16     int to,next,w;
17 }edge[N];
18 
19 void add(int u,int v,int w){
20     edge[cnt].w=w;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;
21 }
22 
23 void init(){
24     cnt=0;
25     memset(head,-1,sizeof(head));
26 }
27 
28 void SPFA(){
29     for(int i=st;i<=en;i++) d[i]=-INF;
30     memset(vis,false,sizeof(vis));
31     d[st]=0;
32     queue <int> Q;
33     Q.push(st);
34     vis[st]=true;
35     while(!Q.empty()){
36         int u=Q.front();
37         Q.pop();
38         vis[u]=false;
39         for(int i=head[u];~i;i=edge[i].next){
40             int v=edge[i].to;
41             if(d[v]<d[u]+edge[i].w){
42                 d[v]=d[u]+edge[i].w;
43                 if(!vis[v]){
44                     Q.push(v);
45                     vis[v]=true;
46                 }
47             }
48         }
49     }
50 }
51 
52 int main(){
53     init();
54     scanf("%d%d",&k,&n);
55     for(int i=1;i<=n;i++){
56         int a,b,c;
57         scanf("%d%d%d",&a,&b,&c);
58         add(a-1,b,c);
59         st=min(st,a-1);
60         en=max(en,b);
61     }
62     for(int i=st;i<en;i++){
63         add(i,i+1,0);
64         add(i+1,i,-1);
65     }
66     SPFA();
67     printf("%d\n",d[en]);
68     return 0;
69 }
View Code

例题2:http://bailian.openjudge.cn/practice/1364

题解:最短路式子:$d[v]<=d[u]+w$

式子1:$sum[a+b+1]-sum[a]>c$      —      $sum[a]<=sum[a+b+1]-c-1$       —      $a+b+1\quad   a\quad   -c-1$

式子2:$sum[a+b+1]-sum[a]<c$      —      $sum[a+b+1]<=sum[a]+c-1$      —      $a\quad  a+b+1\quad  c-1$

注意:先移项,移项完后再处理没有等于的情况。

 1 #include <queue>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 const int N=1234;
 9 const int INF=0x3f3f3f3f;
10 int n,m,cnt;
11 int head[2*N],d[N],Time[N];
12 bool vis[N];
13 
14 struct Edge{
15     int to,next,w;
16 }edge[N];
17 
18 void add(int u,int v,int w){
19     edge[cnt].w=w;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;
20 }
21 
22 void init(){
23     cnt=0;
24     memset(Time,0,sizeof(Time));
25     memset(head,-1,sizeof(head));
26 }
27 
28 bool SPFA(int st){
29     for(int i=0;i<N;i++) d[i]=INF;
30     memset(vis,false,sizeof(vis));
31     queue <int> Q;
32     Q.push(st);
33     d[st]=0;
34     vis[st]=true;
35     Time[st]=1;
36     while(!Q.empty()){
37         int u=Q.front();
38         Q.pop();
39         vis[u]=false;
40         for(int i=head[u];~i;i=edge[i].next){
41             int v=edge[i].to;
42             if(d[v]>d[u]+edge[i].w){
43                 d[v]=d[u]+edge[i].w;
44                 if(!vis[v]){
45                     Q.push(v);
46                     vis[v]=true;
47                     Time[v]++;
48                     if(Time[v]>n) return false;
49                 }
50             }
51         }
52     }
53     return true;
54 }
55 
56 int main(){
57     while(scanf("%d",&n)!=EOF&&n){
58         init();
59         scanf("%d",&m);
60         char op[10];
61         for(int i=1;i<=n;i++) add(0,i,0);
62         for(int i=1;i<=m;i++){
63             int a,b,c;
64             scanf("%d%d%s%d",&a,&b,&op,&c);
65             if(op[0]=='g') add(a+b+1,a,-c-1);
66             else add(a,a+b+1,c-1);
67         }
68         if(SPFA(0)) printf("lamentable kingdom\n");
69         else printf("successful conspiracy\n");
70     }
71     return 0;
72 }
View Code

例题3:http://acm.hdu.edu.cn/showproblem.php?pid=3666

题解:由题意得:$L<=c[i][j]*a[i]/b[j]<=U$ 两边除以$c[i][j]$    —    $L/c[i][j] <= a[i]/b[j] <= U/c[i][j]$,先两边取对数,得到$log(L/c[i][j])\quad  <=\quad  log(a[i])-log(b[j])\quad  <=\quad  log(U/c[i][j])$,推导出两个式子:

式子1:$log(a[i])<=log(U/c[i][j])+log(b[j])$

式子2:$log(b[j])<=log(a[i])-log(L/c[i][j])$

注意:log取double型,n个a和m个b连接,保证了图的连通性,不需要新建边。数据范围注意,有$n*m$个点和$2*n*m$条边。还有注意剪枝,具体传送门

 1 #include <queue>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 const int M=1e6+10;
 9 const int N=400+10;
10 const double INF=1e12;
11 int n,m,cnt,head[M];
12 double l,r,c[N][N],d[N];
13 bool vis[N];
14 
15 struct Edge{
16     int to,next;
17     double w;
18 }edge[M];
19 
20 void add(int u,int v,double w){
21     edge[cnt].w=w;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;
22 }
23 
24 void init(){
25     cnt=0;
26     memset(head,-1,sizeof(head));
27 }
28 
29 bool SPFA(int st){
30     int CNT=0;
31     for(int i=0;i<N;i++) d[i]=INF;
32     memset(vis,false,sizeof(vis));
33     queue <int> Q;
34     Q.push(st);
35     d[st]=0;
36     vis[st]=true;
37     while(!Q.empty()){
38         CNT++;
39         if(CNT>2*(n+m)) return false;
40         int u=Q.front();
41         Q.pop();
42         vis[u]=false;
43         for(int i=head[u];~i;i=edge[i].next){
44             int v=edge[i].to;
45             if(d[v]>d[u]+edge[i].w){
46                 d[v]=d[u]+edge[i].w;
47                 if(!vis[v]){
48                     Q.push(v);
49                     vis[v]=true;
50                 }
51             }
52         }
53     }
54     return true;
55 }
56 
57 int main(){
58 
59     while(scanf("%d%d%lf%lf",&n,&m,&l,&r)!=EOF){
60         init();
61         l=log(l);r=log(r);
62         for(int i=1;i<=n;i++){
63             for(int j=1;j<=m;j++){
64                 scanf("%lf",&c[i][j]);
65                 add(i,j+n,log(1.0*c[i][j])-l);
66                 add(j+n,i,r-log(1.0*c[i][j]));
67             }
68         }
69         if(SPFA(1)) printf("YES\n");
70         else printf("NO\n");
71     }
72 
73     return 0;
74 }
View Code

例题4:http://acm.hdu.edu.cn/showproblem.php?pid=1384

与第一题相同。

 1 #include <queue>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <iostream>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 const int INF=0x3f3f3f3f;
 9 const int N=2e5+10;
10 int k,n,cnt;
11 int st=INF,en=-INF;
12 bool vis[N];
13 int head[2*N],d[N];
14 
15 struct Edge{
16     int to,next,w;
17 }edge[N];
18 
19 void add(int u,int v,int w){
20     edge[cnt].w=w;edge[cnt].to=v;edge[cnt].next=head[u];head[u]=cnt++;
21 }
22 
23 void init(){
24     cnt=0;
25     memset(head,-1,sizeof(head));
26 }
27 
28 void SPFA(){
29     for(int i=st;i<=en;i++) d[i]=-INF;
30     memset(vis,false,sizeof(vis));
31     d[st]=0;
32     queue <int> Q;
33     Q.push(st);
34     vis[st]=true;
35     while(!Q.empty()){
36         int u=Q.front();
37         Q.pop();
38         vis[u]=false;
39         for(int i=head[u];~i;i=edge[i].next){
40             int v=edge[i].to;
41             if(d[v]<d[u]+edge[i].w){
42                 d[v]=d[u]+edge[i].w;
43                 if(!vis[v]){
44                     Q.push(v);
45                     vis[v]=true;
46                 }
47             }
48         }
49     }
50 }
51 
52 int main(){
53     while(scanf("%d",&n)!=EOF){
54         init();
55         for(int i=1;i<=n;i++){
56             int a,b,c;
57             scanf("%d%d%d",&a,&b,&c);
58             add(a-1,b,c);
59             st=min(st,a-1);
60             en=max(en,b);
61         }
62         for(int i=st;i<en;i++){
63             add(i,i+1,0);
64             add(i+1,i,-1);
65         }
66         SPFA();
67         printf("%d\n",d[en]);
68     }
69     return 0;
70 }
View Code

猜你喜欢

转载自www.cnblogs.com/ehanla/p/9134012.html