AcWing 算法提高课 差分约束专题

差分约束

原理

1. 求不等式组的可行解

求形如 xixj+ck(两自变量,一常量)

该问题转化为,求从j 走到i 长度为ck的一条边,的最短路问题

必须满足从源点出发能够走到所有的边

Step1: 讲不等式 xixj+ck转化为从xj 走到xi 长度为ck的一条边

Step2: 建立虚拟源点,使得该点出发可以到达所有边

Step3: 从源点出发做一遍单源最短路

  1. 存在负环,原不等式一定无解
  2. 无负环,dis[i]就是一个解

2. 求每个变量的maxormin

求最小值用最长路;求最大值用最短路

  1. max: 转化 xic: 建立超级源点 0,然后建立0i,长度为 c 的边,有不等式链 xixj+c1xk+c2+c1x0+c1+c2+...+cm,即 求上界最小值
  2. min: 转化 xic: 建立超级源点 0,然后建立0i,长度为 c 的边,有不等式链 xixj+c1xk+c2+c1x0+c1+c2++cm, 即 求下界最大值

糖果

题目

题目
Code

思路

题目的约束条件可以进行如下转化:

{A=BBAABx=1A<BBA+1x=2ABABx=3A>BAB+1x=4ABBAx=5

绝对值:x1xx0+1

队列过不了,得用栈优化

Code

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);

using namespace std;
typedef long long ll;
const int N = 1e5 + 5, M = 3e5 + 5;//不是很懂为什么是这么大
int n, k;
int h[N], e[M], ne[M], w[M], idx;
bool vis[N];
int cnt[N];
ll dis[N];
stack <int> q;

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

bool spfa () {

    memset (dis, -0x3f, sizeof dis);
    dis[0] = 0;
    q.push (0), vis[0] = true; //和传统不一样

    while (!q.empty()) {
        int t = q.top();
        q.pop();
        vis[t] = false;

        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            //最小值最大
            if (dis[j] < dis[t] + w[i]) {
                dis[j] = dis[t] + w[i];
                cnt[j] = cnt[t] + 1;

                if (cnt[j] >= n + 1) //注意是n+1
                    return false;
                if (!vis[j])
                    q.push (j), vis[j] = true;
            }
        }
    }
    return true;
}

int main () {
    IOS;
    memset (h, -1, sizeof h);
    cin >> n >> k;
    while (k --) {
        int op, a, b;
        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);
    }
    //造虚拟源点
    for (int i = 1; i <= n; i ++)
        add (0, i, 1);

    if (!spfa()) 
        cout << -1 << endl;
    else {
        long long ans  = 0;
        for (int i = 1; i <= n; i ++)
            ans += dis[i];
        cout << ans << endl;
    }
}
//最大值最小

区间

题目

给定 n 个区间 [ai,bi] 和 n 个整数 ci。
你需要构造一个整数集合 Z,使得 ∀i∈[1,n],Z 中满足 aixbi 的整数 x 不少于 ci 个。
求这样的整数集合 Z 最少包含多少个数。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含三个整数 ai,bi,ci。

输出格式
输出一个整数表示结果。

数据范围
1n50000,
0ai,bi50000,
1cibiai+1

输入样例:
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
输出样例:
6

思路

Code

Si表示1i中被选出的数的个数,S0=0,即求S50001的最小值

  1. SiSi1,1i50001
  2. Si1Si1
  3. SbSa1cSbSa1+c

注:a ++, b ++; // 避免0号位出现歧义

Code

//最小值 最长路
#include <bits/stdc++.h>

using namespace std;
const int N = 5e4 + 5, M = N * 3;
int n;
int h[N], e[M], ne[M], w[M], idx;
queue <int> q;
bool vis[N];
int dis[N], cnt[N];

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

void spfa () {
    memset (dis, -0x3f, sizeof h);
    q.push (0), dis[0] = 0, vis[0] = true;

    while (!q.empty()) {
        int t = q.front();
        q.pop();
        vis[t]=  false;

        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dis[j] < dis[t] + w[i]) {
                dis[j] = dis[t] + w[i];
                cnt[j] = cnt[j] + 1;

                // if (cnt[j] >= n + 1)
                //     return false;
                if (!vis[j])
                    q.push (j), vis[j] = true;
            }
        }
    }
    //return true;
}

int main () {
    memset (h, -1, sizeof h);
    cin >> n;
    dis[1] = 0;
    for (int i = 1; i < N; i ++) { //注意是到N
        add (i - 1, i, 0);
        add (i, i - 1, -1);
    }

    for (int i = 1; i <= n; i ++) {
        int a, b, c;
        cin >> a >> b >> c;
        a ++, b ++; //避免0号位出现歧义
        add (a - 1, b, c);
    }

    spfa ();
    cout << dis[50001] << endl;

}

排队布局

题目

https://www.luogu.com.cn/problem/P4878

思路

Code

最大值,最短路问题

限制条件:xbxaLxbxaD

  1. xixi+1,1i<b
  2. xbxa+L
  3. xaxbD

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 1005, M = N + 2e4;
int n, L, D;
int dis[N], cnt[N];
bool vis[N];
int h[N], e[M], ne[M], w[M], idx;

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

bool spfa (int x) {
    queue <int> q;
    memset (dis, 0x3f, sizeof dis);
    memset (cnt, 0, sizeof cnt);
    memset (vis, false, sizeof vis);

    for (int i = 1; i <= x; i ++) //x
        q.push (i), vis[i] = true, dis[i] = 0;

    while (!q.empty()) {
        int t = q.front();
        q.pop();
        vis[t] = false;

        for (int i = h[t]; ~i; i = ne[i]) {
            int j = e[i];
            if (dis[j] > dis[t] +w[i]) {
                dis[j] = dis[t] + w[i];
                cnt[j] = cnt[t] + 1;

                if (cnt[j] >= n)
                    return false;

                if (!vis[j])
                    q.push (j), vis[j] = true;
            }
        }
    }
    return true;
}

int main () {
    memset (h, -1, sizeof h);
    cin >> n >> L >> D;
    for (int i = 1; i < n; i ++)
        add (i + 1, i, 0);
    while (L --) {
        int a, b, c;
        cin >> a >> b >> c;
        if (a > b)
            swap (a, b);
        add (a, b, c); //注意最短路的符号是相反的
    }
    while (D --) {
        int a, b, c;
        cin >> a >> b >> c;
        if (a > b)
            swap (a, b);
        add (b, a, -c);
    }
    if (!spfa(n)) 
        puts("-1");
    else {
        spfa(1);
        if (dis[n] == 0x3f3f3f3f )   
            puts("-2");
        else 
            cout << dis[n] << endl;
    }
}

雇佣收营员

还没做,嘿嘿

posted @   Sakana~  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示