【图论】差分约束算法详解

一、前言

在介绍差分约束之前,我们首先需要知道差分约束是用来解决什么问题的:差分约束是一个用来解决形如 X<=Y+c 的二元不等式组的可行解的一个算法。在高中数学中我们会学习如何使用线性规划的方法来解决这个问题来求出不等式的可行解或者最大最小解。而在ACM中,差分约束算法可以将求解不等式组的问题转化为图论问题,通过图论中的知识来解决。

二、差分约束

1.求不等式组可行解的前提条件
  • 将每一个未知数转化为图论上的一个点,根据不等式组建立一个有向图(建立有向图的具体方法将在后文中提及)
  • 建立一个虚拟超级原点,从该原点出发可以到达有向图中的每一条边,一般建立一个原点,该点到每一个点都有一条长度为0的有向边。
  • 对于每一个未知数X,从原点出发到X的距离就是X的一个满足条件的可行解。
2.算法步骤
  • 先根据原不等式组建立有向图(建图的方法将在后文提及)
  • 建立一个超级源点
  • 使用SPFA算法从源点求单源最短(长)路
    • 结果1:有向图存在负环,不等式无解
    • 结果2:没有负环,dis[i] 就是原不等式组的一个可行解

注意:
1.图中存在点在求最短(最长)路后的距离仍然为INF(-INF),代表该未知数的取值不受任何约束,可以取任意值。
2.在实际解题时一定要找准找全题目中所有的不等式约束关系

3.建立有向图
1)求最大可行解
  • 最大可行解:所有的可行解中某个未知数X的值最大
  • 未知数X的最大可行解为:从超级源点到点X的最短路dis[x]
  • 有向图建立方法:
    • 将所有的不等式转化为X <= Y + c 的形式
    • 建立有向边Y-->X,长度为c
    • 注意一定是<=,如果是X < Y可以转化为X <= Y - 1
    • 如果存在不等式形如c <= Y,转化为X0 <= Y - c,X0为超级源点。
2)求最小可行解
  • 最小可行解:所有的可行解中某个未知数X的值最小
  • 未知数X的最小可行解为:从超级源点到点X的最长路dis[x]
  • 有向图建立方法:
    • 将所有的不等式转化为X + c <= Y 的形式
    • 建立有向边X-->Y,长度为c
    • 注意一定是<=,如果是X < Y可以转化为X + 1 <= Y
    • 如果存在不等式形如Y <= c,转化为Y - c <= X0,X0为超级源点。
3)证明
  • 以求xi的最大值为例:求从xi出发,构成的不等式链xi <= xj + c1 <= xk + c1 +c2 <= ...... <= c1+c2+c3+...+cn,求出来的上界的最小值就是可行解的最大值。
代码(结合例题)
糖果

幼儿园里有 N 个小朋友,老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。

但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候, 老师需要满足小朋友们的 K 个要求。

幼儿园的糖果总是有限的,老师想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。

输入格式
输入的第一行是两个整数 N,K。

接下来 K 行,表示分配糖果时需要满足的关系,每行 3 个数字 X,A,B。

如果 X=1.表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多。
如果 X=2,表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果。
如果 X=3,表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果。
如果 X=4,表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果。
如果 X=5,表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果。
小朋友编号从 1 到 N。

输出格式
输出一行,表示老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 −1。

数据范围
1≤N<1e5,
1≤K≤1e5,
1≤X≤5,
1≤A,B≤N
输入样例:
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
输出样例:
11

正确代码

// Problem: 糖果
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1171/
// Memory Limit: 64 MB
// Time Limit: 1000 ms

//#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define endl '\n'
#define fi first
#define se second
#define pb push_back
#define int long long
#define INF 0x3f3f3f3f
#define ull unsigned long long
#define print(x) cout << (x) << endl
#define all(x) (x).begin(), (x).end()
#define mem(a, b) memset(a, b, sizeof(a))
#define f(i, l, r) for (int i = (l); i <= (r); i++)
#define ff(i, l, r) for (int i = (l); i >= (r); i--)
#define pr(x, n) f(_, 1, n) cout << (x[_]) << " \n"[_ == n];
#define ck(x) cerr << #x << "=" << x << '\n';
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 7, mod = 1e9 + 7;

int n, k;
int h[N], e[N], ne[N], v[N], id;
int dis[N];
int cnt[N];
bool in_st[N];

void add(int a, int b, int c)
{
  ne[id] = h[a], h[a] = id, e[id] = b, v[id++] = c;
}

bool spfa()
{
  mem(dis, -0x3f);
  stack<int> st;

  dis[0] = 0;
  st.push(0);
  in_st[0] = true;
  while (st.size())
  {
    int te = st.top();
    st.pop();
    in_st[te] = false;
    for (int i = h[te]; ~i; i = ne[i])
    {
      int j = e[i];
      if (dis[j] < dis[te] + v[i])
      {
        dis[j] = dis[te] + v[i];
        cnt[j] = cnt[te] + 1;
        if (cnt[j] > n)
        {
          return false;
        }
        if (!in_st[j])
        {
          st.push(j);
          in_st[j] = true;
        }
      }
    }
  }
  return true;
}

void solve()
{
  mem(h, -1);
  cin >> n >> k;
  f(i, 1, n)
  {
    add(0, i, 1);
  }
  while (k--)
  {
    int a, b, op;
    cin >> op >> a >> b;
    if (op == 1)
      add(a, b, 0), add(b, a, 0);
    else if (op == 2)
      add(a, b, 1);
    else if (op == 3)
      add(b, a, 0);
    else if (op == 4)
      add(b, a, 1);
    else
      add(a, b, 0);
  }
  if (spfa())
  {
    int ans = 0;
    f(i, 1, n) ans += dis[i];
    print(ans);
  }
  else
    print(-1);
}

signed main()
{
  std::ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  clock_t start_time = clock();
  int __ = 1;
  //    cin>>__;
  //    init();
  while (__--)
    solve();
  clock_t end_time = clock();
  // cerr << "Running time is: " << ( double )(end_time - start_time) / CLOCKS_PER_SEC * 1000 << "ms" << endl;
  return 0;
}

作者:Avalon·Demerzel
更多文章详见专栏:图论与数据结构

  • 11
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MATLAB中的约束优化算法是一种用于解决带有约束条件的优化问题的方法。它可以用于处理多种不同类型的约束,例如等式约束、不等式约束以及混合约束。 在MATLAB中,我们可以使用两种主要的约束优化算法:基于积分的法向搜索方法和基于内点的仿牛顿方法。 基于积分的法向搜索方法是一种迭代的优化算法,它通过沿着搜索方向更新解,并根据目标函数和约束条件的梯度信息来确定搜索方向。这种方法的优点是易于实现和调试,但可能会受到初始点的选择和局部极小值的影响。 基于内点的仿牛顿方法是一种使用内点策略和近似牛顿法的算法。内点方法通过在可行域内的某个点引入一个正值的惩罚项,将约束问题转化为无约束问题。这种方法的优点是可以处理复杂的非线性约束,并且能够在全局范围进行全局优化。 MATLAB中还提供了一些特定问题的约束优化算法,例如非线性规划、整数规划和混合整数规划等。这些算法基于不同的优化原理和求解策略,可以根据具体问题的特点选择最适合的算法进行求解。 总之,MATLAB提供了多种约束优化算法,可以根据具体问题的特点和要求选择适合的算法进行求解。这些算法的应用可以在工程、经济、金融等领域中发挥重要作用,并为求解复杂的优化问题提供了有效的工具和方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值