BZOJ2330 [SCOI2011]糖果(差分约束模板)

本文介绍了一种通过构建差分约束系统解决不等式问题的方法,并详细解释了如何将问题转化为图论模型进行求解的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Address


Solution

  • 先来考虑一个简单的问题,给出这三个关系式,求 AC A − C 的最小值。
    (1)(2)(3) AC4 AB3 BC2 { ( 1 )   A − C ≥ 4 ( 2 )   A − B ≥ 3 ( 3 )   B − C ≥ 2
  • 显然我们可以得到:
    {(1)(2)+(3) AC4 AC5 { ( 1 )   A − C ≥ 4 ( 2 ) + ( 3 )   A − C ≥ 5
  • 因此 AC A − C 的最小值为 max(4,5)=5 max ( 4 , 5 ) = 5
  • 进一步的,我们可以把形如 AB>=x A − B >= x 的式子转化为图论的模型,即点 B B 向点 A 连一条权值为 x x 的边,那么对于上面的关系式,建图如下:
    • CA,权值为4。
    • BA B → A ,权值为3。
    • CB C → B ,权值为2。
    • 我们会发现这时 AC A − C 的最小值就是点 C C 到点 A最长路
    • 像这样用图来求解不等式关系的方法叫做差分约束,丢个网址
    • 因为这题也是要求最小值,我们同样考虑转成大于等于的不等式并求最长路(边权取负跑最短路)。
    • 那么对于题目中五种操作:
    • (1)(2)(3)(4)(5) A=B A<B AB A>B AB { ( 1 )   A = B ( 2 )   A < B ( 3 )   A ≥ B ( 4 )   A > B ( 5 )   A ≤ B

      • 就可以转变为:

      (1)(2)(3)(4)(5) AB0,BA0 BA1 AB0 AB1 BA0 { ( 1 )   A − B ≥ 0 , B − A ≥ 0 ( 2 )   B − A ≥ 1 ( 3 )   A − B ≥ 0 ( 4 )   A − B ≥ 1 ( 5 )   B − A ≥ 0

      • 对应连边即可。
      • 因为每个小朋友手上都要有糖果,增加一个起点向其它所有点连一条权值为1的边。
      • 无解的情况有两种:
        1. 图中存在负环(用 DFS D F S SPFA S P F A 判断)。
        2. 出现了形如 A>A A > A A<A A < A 的不等式。
      • 然而用(sang)心(xin)良(bing)苦(kuang)的出题人设了点卡普通 SPFA S P F A ,仿佛还要加 SLF S L F 优化(设要加入的节点是 j j ,队首元素为 i,若 dist(j)dist(i) d i s t ( j ) ≤ d i s t ( i ) ,则将 j j 插入队首,否则插入队尾,可以用 STL 或者手写循环队列模拟)。

      Code

      #include <iostream>
      #include <cstdio>
      #include <cctype>
      #include <cstring> 
      #include <algorithm>
      
      using namespace std;
      
      namespace inout
      {
          const int S = 1 << 20;
          char frd[S], *ihed = frd + S;
          const char *ital = ihed;
      
          inline char inChar()
          {
              if (ihed == ital)
                  fread(frd, 1, S, stdin), ihed = frd;
              return *ihed++;
          }
      
          inline int get()
          {
              char ch; int res = 0; bool flag = false;
              while (!isdigit(ch = inChar()) && ch != '-');
              (ch == '-' ? flag = true : res = ch ^ 48);
              while (isdigit(ch = inChar()))
                  res = res * 10 + ch - 48;
              return flag ? -res : res;
           } 
      };
      using namespace inout;
      
      typedef long long ll;
      const int N = 1e5 + 5, M = N << 3;
      const int Maxn = 0x3f3f3f3f;
      bool vis[N]; int dis[N], h[M], n, k, m; ll Ans;
      
      struct Edge 
      {
          int to, cst; Edge *nxt;
      }p[M], *T = p, *lst[N];
      
      inline void Link(int x, int y, int z)
      {
          (++T)->nxt = lst[x]; lst[x] = T; T->to = y; T->cst = z;
      }
      
      inline bool dfs_Spfa(int x)
      {
          vis[x] = true; int y;
          for (Edge *e = lst[x]; e; e = e->nxt)
              if (dis[y = e->to] > dis[x] + e->cst)
              {
                  dis[y] = dis[x] + e->cst;
                  if (vis[y] || dfs_Spfa(y)) return true;
              }
          return vis[x] = false;
      }
      
      inline void bfs_Spfa()
      {
          memset(vis, false, sizeof(vis));
          memset(dis, Maxn, sizeof(dis));
          dis[h[1] = 0] = 0; int t = 0, w = 1, x, y;
          while (t != w)
          {
              if (++t == M) t = 0;
              vis[x = h[t]] = false;
              for (Edge *e = lst[x]; e; e = e->nxt)
                  if (dis[y = e->to] > dis[x] + e->cst)
                  {
                      dis[y] = dis[x] + e->cst;
                      if (!vis[y]) 
                      {
                          if (dis[y] > dis[h[t + 1]])
                          {
                              if (++w == M) w = 0;
                              vis[h[w] = y] = true;
                          }
                          else 
                          {
                              vis[h[t] = y] = true;
                              if (!t--) t = M - 1; 
                          }
      
                      }
                  }
          }
      }
      
      int main()
      {
      //  freopen("candy.in", "r", stdin);
      //  freopen("candy.out", "w", stdout);
      
          n = get(); m = get(); int k, x, y;
          while (m--)
          {
              k = get(); x = get(); y = get();
              switch (k)
              {
                  case 1: Link(x, y, 0); Link(y, x, 0); break;
                  case 2: if (x == y) return puts("-1"), 0; Link(x, y, -1); break;
                  case 3: Link(y, x, 0); break;
                  case 4: if (x == y) return puts("-1"), 0; Link(y, x, -1); break;
                  case 5: Link(x, y, 0); break; 
              }
          }
          for (int i = 1; i <= n; ++i) Link(0, i, -1);
      
          bool flag = false;
          for (int i = 1; i <= n && !(flag = dfs_Spfa(i)); ++i);
          if (flag) return puts("-1"), 0;
      
          bfs_Spfa();
          for (int i = 1; i <= n; ++i) Ans += (ll)dis[i];
          cout << -Ans << endl;
      
      //  fclose(stdin); fclose(stdout);
          return 0; 
      }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值