差分约束系统

亦凉 2023-07-06 13:09 72阅读 0赞

参考博客:https://blog.csdn.net/dragon60066/article/details/80245797
此知识点需对最短路,最长路有了解。
差分约束系统应用一,
知道形如下面的不等式组
在这里插入图片描述
现在我们要求一组解x1=a1,x2=a2,这样的解来满足上面不等式组。
有一个性质,我们可以知道,如果有一组解x1=a1,x2=a2~~满足上面不等式组,那么这组解全部+d。即x1=a1+d,x2=a2+d,也是可满足的。因为做差之后d会被抵消掉。
现在,我们分析一个不等式
x1-x2<=k 转化为x1<=x2 +k在转化x2>=x1-k;
此不等式是不是很眼熟呢。我们回忆在最短路的学习中,是不是有dis[v] >=dis[u] + k;这个不等式组呢。此不等式组在最短路中的意义为:
到v的最短路>=到u的最短路+(u到v的比边权)。
我们可以注意到u到v的边权为k。
那么我们是不是可以像最短路一样,
给x1到x2连上一条边权为-k的边呢。
我们通过此种建图之后,如何继续判断是否存在一组解满足不等式组呢?
我们综合最短路的应用,我们是不是可以跑最短路spfa算法,来判断,该图中是否存在负环,如果存在负环,那么一定是无解的。
存在负环的情况为:a>b,b>c,c>a。可画图,就可以理解为何存在负环不行。

例题:https://www.luogu.com.cn/problem/P1993

  1. #include"stdio.h"
  2. #include"string.h"
  3. #include"algorithm"
  4. using namespace std;
  5. const int N = 100100;
  6. const int INF = 1e9 + 7;
  7. int n,m,ans;
  8. int head[N],ver[N * 10],Next[10 * N],edge[N * 10],tot;
  9. int vis[N],dist[N];
  10. void add(int x,int y,int w){
  11. ver[++ tot] = y; edge[tot] = w;
  12. Next[tot] = head[x]; head[x] = tot;
  13. }
  14. void dfs(int x){
  15. vis[x] = 1;
  16. for(int i = head[x]; i; i = Next[i])
  17. {
  18. int y = ver[i],w = edge[i];
  19. if(dist[y] > dist[x] + w){
  20. if(vis[y] || ans) {ans = 1;break;}
  21. dist[y] = dist[x] + w;
  22. dfs(y);
  23. }
  24. }
  25. }
  26. int main()
  27. {
  28. scanf("%d%d",&n,&m);
  29. for(int i = 1; i <= n; i ++)
  30. dist[i] = INF;
  31. for(int i = 1; i <= m; i ++)
  32. {
  33. int op; scanf("%d",&op);
  34. if(op == 3) {
  35. int x,y; scanf("%d%d",&x,&y);
  36. add(x,y,0); add(y,x,0);
  37. } else if(op == 1){
  38. int x,y,w; scanf("%d%d%d",&x,&y,&w);
  39. add(x,y,-w);
  40. } else {
  41. int x,y,w; scanf("%d%d%d",&x,&y,&w);
  42. add(y,x,w);
  43. }
  44. }
  45. ans = 0;
  46. for(int i = 1; i <= n; i ++){
  47. memset(vis,0,sizeof(vis));
  48. dfs(i);
  49. if(ans) break;
  50. }
  51. if(ans == 1) printf("No\n");
  52. else printf("Yes\n");
  53. }

例题二:https://www.luogu.com.cn/problem/P3275
参考题解:https://www.luogu.com.cn/problemnew/solution/P3275
从题目中,我们可以得到所有的小朋友一定会分到一个糖果。
同时,题目要求的是最小需要多少个糖果才能满足需求。
我们现在建图时,建(u,v,w)这条边表示顶点v比顶点u大w。
我们观察5种需求类型,
第一种,a==b,add(u,v,0);add(v,u,0);
第二种,a=b add(b,a,0)//如果a>=b,那么同样根据贪心原则,我肯定是让他们越靠近越好。
第四种,a>b add(b,a,1);
第五种,a<=b add(a,b,0);
那么我们建图之后就跑最长路即可。
如下图,
在这里插入图片描述
3到1的路径为1,但是因为2的限制,所以1要比3大2。
同时,在跑spfa的时候判正权环的出现。
正权环即:
1->2 w 2
2->3 w 3
3->1 w 3
如果在此图上跑最长路,那么我们将会一直跑下去,不能结束,所以要判断正权环。

  1. #include"stdio.h"
  2. #include"string.h"
  3. #include"queue"
  4. #include"algorithm"
  5. using namespace std;
  6. typedef long long ll;
  7. inline int read(){
  8. int a=0;char x=getchar();
  9. while(x<'0'||x>'9')x=getchar();
  10. while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar();
  11. return a;
  12. }
  13. const int N = 300100,M = 1001010;
  14. int head[N],ver[M],Next[M],edge[M],tot;
  15. int n,k,dist[N],vis[N];
  16. int cnt[N];
  17. void add(int x,int y,int w){
  18. ver[++ tot] = y; Next[tot] = head[x];
  19. edge[tot] = w; head[x] = tot;
  20. }
  21. bool bfs()
  22. {
  23. queue<int> Q; Q.push(0);
  24. dist[0] = 0;vis[0] = 1;
  25. while(!Q.empty())
  26. {
  27. int x = Q.front(); Q.pop();
  28. vis[x] = 0;
  29. cnt[x] ++;
  30. if(cnt[x] >= n){
  31. return false;
  32. }
  33. for(int i = head[x]; i; i = Next[i]){
  34. int y = ver[i],w = edge[i];
  35. if(dist[y] < dist[x] + w){
  36. dist[y] = dist[x] + w;
  37. if(vis[y] == 0){
  38. vis[y] = 1; Q.push(y);
  39. }
  40. }
  41. }
  42. }
  43. return true;
  44. }
  45. int main()
  46. {
  47. n = read(); k = read();
  48. for(int i = 1; i <= k; i ++)
  49. {
  50. int op,a,b;
  51. op = read(); a = read(); b = read();
  52. if(op == 1) add(a,b,0),add(b,a,0);
  53. else if(op == 2) add(a,b,1);
  54. else if(op == 3) add(b,a,0);
  55. else if(op == 4) add(b,a,1);
  56. else add(a,b,0);
  57. if(op == 2 || op == 4)
  58. {
  59. if(a == b) {printf("-1\n"); return 0;}
  60. }
  61. }
  62. for(int i = n; i >= 1; i --)add(0,i,1);
  63. if(bfs() == false){
  64. printf("-1\n");
  65. } else
  66. {
  67. ll ans = 0;
  68. for(int i = 1; i <= n; i ++) ans += (ll)dist[i];
  69. printf("%lld\n",ans);
  70. }
  71. }

例题三:https://www.cnblogs.com/five20/p/9173155.html
例题:https://www.luogu.com.cn/problem/P2294
很容易的想到建图方式:
w[u,v] = k,表示v大u,k个单位。
同时隐含着w[v,u] = -k。这一条件。
把这两个图建出来,发现,如果有环,那就不合法。

  1. #include"stdio.h"
  2. #include"string.h"
  3. #include"queue"
  4. #include"algorithm"
  5. using namespace std;
  6. typedef long long ll;
  7. inline int read(){
  8. int a=0;char x=getchar();bool f=0;
  9. while((x<'0'||x>'9')&&x!='-')x=getchar();
  10. if(x=='-')x=getchar(),f=1;
  11. while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar();
  12. return f?-a:a;
  13. }
  14. const int N = 300100,M = 1001010;
  15. const int INF = 1000000100;
  16. int head[N],ver[M],Next[M],edge[M],tot;
  17. int n,k,dist[N],vis[N];
  18. int cnt[N];
  19. void add(int x,int y,int w)
  20. {
  21. ver[++ tot] = y;
  22. Next[tot] = head[x];
  23. edge[tot] = w;
  24. head[x] = tot;
  25. }
  26. bool bfs(int x)
  27. {
  28. queue<int> Q;
  29. Q.push(x);
  30. for(int i = 0; i <= n; i ++) dist[i] = -INF;
  31. dist[x] = 0;
  32. vis[x] = 1;
  33. while(!Q.empty())
  34. {
  35. int x = Q.front();
  36. Q.pop();
  37. vis[x] = 0;
  38. cnt[x] ++;
  39. if(cnt[x] >= n)
  40. {
  41. return false;
  42. }
  43. for(int i = head[x]; i; i = Next[i])
  44. {
  45. int y = ver[i],w = edge[i];
  46. if(dist[y] < dist[x] + w)
  47. {
  48. dist[y] = dist[x] + w;
  49. if(vis[y] == 0)
  50. {
  51. vis[y] = 1;
  52. Q.push(y);
  53. }
  54. }
  55. }
  56. }
  57. return true;
  58. }
  59. void init()
  60. {
  61. memset(head,0,sizeof(head));
  62. memset(vis,0,sizeof(vis));
  63. memset(cnt,0,sizeof(cnt));
  64. tot = 0;
  65. }
  66. int main()
  67. {
  68. int T;
  69. T = read();
  70. while(T --)
  71. {
  72. init();
  73. n = read();
  74. k = read();
  75. for(int i = 1; i <= k; i ++)
  76. {
  77. int a,b,w;
  78. a = read(); b = read();w = read();
  79. add(a - 1,b,w); add(b,a - 1,-w);
  80. }
  81. int mark = 1;
  82. for(int i = 0; i <= n; i ++){
  83. if(cnt[i] == 0){
  84. if(bfs(i) == false) {mark = 0;break;}
  85. }
  86. }
  87. if(mark == 1) printf("true\n");
  88. else printf("false\n");
  89. }
  90. return 0;
  91. }

发表评论

表情:
评论列表 (有 0 条评论,72人围观)

还没有评论,来说两句吧...

相关阅读

    相关 约束系统详解

    一直不知道差分约束是什么类型题目,最近在写最短路问题就顺带看了下,原来就是给出一些形如x-y<=b不等式的约束,问你是否满足有解的问题 好神奇的是这类问题竟然可以转换成图论里

    相关 约束系统C++实现

    差分约束:线性规划矩阵A的每一行包含一个1与一个-1,其他元素为0.因此,由Ax<=b给出的约束条件是m个差分约束集合,其中包含n个未知元。每个约束条件为不等式: xj-x