当前位置: 首页 > article >正文

差分约束系统 + spfa求最短路

首先先来个具体的题目来引入一下。

Intervals(UVA1723)

题面翻译

n n n 个区间,在区间 [ a i , b i ] [a_i,b_i] [ai,bi] 中至少取任意互不相同的 c i c_i ci 个整数。求在满足 n n n 个区间的情况下,至少要取多少个正整数。

输入格式

本题有多组数据

第一行的一个整数 T T T 表示数据个数,后面有一行空行。

对于每组数据:

第一行包含一个整数 n ( 1 ≤ n ≤ 50000 ) n(1\leq n\leq 50000) n(1n50000) 表示区间数。

以下 n n n 行描述区间。

输入的第 i + 1 i+1 i+1 行包含三个整数 a i , b i , c i a_i,b_i,c_i ai,bi,ci,由空格分开。其中 0 ≤ a i ≤ b i ≤ 50000 , 1 ≤ c i ≤ b i − a i + 1 0\leq a_i\leq b_i\leq 50000,1\leq c_i\leq b_i-a_i+1 0aibi500001cibiai+1

输出格式

对于每组数据,输出一个对于 n n n 个区间 [ a i , b i ] [a_i,b_i] [ai,bi]
至少取 c i c_i ci 个不同整数的数的总个数。

在除了最后一组数据后输出空行。

样例

input:

1

5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

output:

6

样例解释

可以取 3 , 4 , 5 , 8 , 9 , 10 3,4,5,8,9,10 3,4,5,8,9,10,为符合条件且取数个数最少的一组解。

题目模型的转换

我们建立一个新的数组, d [ i ] d[i] d[i] 表示 [ 0 , i − 1 ] [0, i - 1] [0,i1] 这个区间内一共被选中了多少个数字,我们用题目中给出来的样例来举例子。

5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

通过上面的这些数据,我们就能知道 d [ i ] d[i] d[i] 需要满足以下这些条件。

{ d [ 8 ] − d [ 3 ] ≥ 3 d [ 11 ] − d [ 8 ] ≥ 3 d [ 9 ] − d [ 6 ] ≥ 1 d [ 4 ] − d [ 1 ] ≥ 1 d [ 12 ] − d [ 10 ] ≥ 1 \left\{\begin{aligned} d[8] - d[3] \ge 3 \\ d[11]-d[8] \ge 3 \\ d[9] - d[6] \ge 1 \\ d[4] - d[1] \ge 1 \\ d[12] - d[10] \ge 1 \end{aligned}\right. d[8]d[3]3d[11]d[8]3d[9]d[6]1d[4]d[1]1d[12]d[10]1

然后我们再重新整理一下上面的这些不等式。

{ d [ 8 ] ≥ d [ 3 ] + 3 d [ 11 ] ≥ d [ 8 ] + 3 d [ 9 ] ≥ d [ 6 ] + 1 d [ 4 ] ≥ d [ 1 ] + 1 d [ 12 ] ≥ d [ 10 ] + 1 \left\{\begin{aligned} d[8] \ge d[3] + 3 \\ d[11] \ge d[8] + 3 \\ d[9] \ge d[6] + 1 \\ d[4] \ge d[1] + 1 \\ d[12] \ge d[10] + 1 \end{aligned}\right. d[8]d[3]+3d[11]d[8]+3d[9]d[6]+1d[4]d[1]+1d[12]d[10]+1

除此之外,我们需要注意,因为 d 数组是一个前缀和,所以我们也需要维护好前缀和的合法性,也就是: ∀ i > 0 , d [ i ] ≥ d [ i − 1 ] , d [ i − 1 ] ≥ d [ i ] − 1 \forall i>0,d[i] \ge d[i - 1],d[i - 1]\ge d[i] - 1 i>0,d[i]d[i1],d[i1]d[i]1,这两个式子使得两个相邻的 d 元素,满足前缀和的要求,后面的要么等于前面的,要么比前面的大一。

那么我们只需要使得 d 数组 恰好 满足上面的种种条件,或者说使得 d 数组 恰好 满足上面的种种约束,我们就可以求出此题的答案(例如样例中 d[12] 就是最终结果,因为被选中的数字肯定小于 12)。

用 spfa 求出满足所有约束的 d 数组

我们先来看一下 spfa 的代码。

int spfa()
{
    memset(d, -0x3f3f3f3f, sizeof(d));
    memset(vis, 0, sizeof(vis));
    d[0] = 0, vis[0] = 1;
    queue<int> q;
    q.push(0);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i].first;
            int w = e[u][i].second;
            if (d[v] < d[u] + w) // 注意这里
            {
                d[v] = d[u] + w;
                if (!vis[v])
                {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    for (int i = 0; i <= 50000; i++)
        e[i].clear();
    return d[t];
}

这是一个 spfa 的代码,我们注意代码中出现注释的部分,spfa 会不停的寻找满足这个条件的边,找到了之后呢?我们就会修改 d[v] 的值,使得 d [ v ] ≥ d [ u ] + w d[v] \ge d[u] + w d[v]d[u]+w (大家有没有觉得这个式子特别的熟悉?),直到所有的边全部都恰好满足条件为止,那么我们为什么不能用上面 d 数组中元素两两之间的关系来代替边权呢?

所以此题就有了以下的这个代码。

#include <bits/stdc++.h>
#define mp(a, b) make_pair(a, b)

using namespace std;

const int maxn = 50005;
int dis[maxn], vis[maxn], n, t;

vector<pair<int, int>> e[maxn];

int spfa() // 正常的一个最长路的代码,注意初始化
{
    memset(dis, -0x3f3f3f3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    dis[0] = 0, vis[0] = 1;
    queue<int> q;
    q.push(0);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i].first;
            int w = e[u][i].second;
            if (dis[v] < dis[u] + w)
            {
                dis[v] = dis[u] + w;
                if (!vis[v])
                {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    for (int i = 0; i <= 50000; i++)
        e[i].clear();
    return dis[t];
}

int solve()
{
    cin >> n;
    t = 0;
    for (int i = 1; i <= n; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        e[a].push_back(mp(b + 1, c)); // 题目中给出的约束
        t = max(t, b + 1);            // t 是图中最大的点,所有被选中的数字全部都小于 t
    }
    for (int i = 1; i <= t; i++)
    {
        e[i - 1].push_back(mp(i, 0)); // 为了使得前缀和合法而添加的约束
        e[i].push_back(mp(i - 1, -1));
    }
    int ans = spfa();
    return ans;
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        cout << solve() << '\n';
        if (T) // 输出格式有要求,大家要注意
            cout << '\n';
    }
    return 0;
}









本人能力有限,如有不当之处敬请指教!祝大家新年快乐!


http://www.kler.cn/a/523516.html

相关文章:

  • C++并发编程指南02
  • C语言练习(29)
  • SVG 矩形:深入理解与实际应用
  • Ubuntu二进制部署K8S 1.29.2
  • 使用 Redis 实现分布式锁的基本思路
  • K8s运维管理平台 - KubeSphere 3.x 和4.x 使用分析:功能较强,UI美观
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.19 排序革命:argsort的十大高阶用法
  • React中的JavaScript语法
  • MATLAB中fetchOutputs函数用法
  • 2007-2020年各省国内专利申请授权量数据
  • 【MySQL — 数据库增删改查操作】深入解析MySQL的 Update 和 Delete 操作
  • 【C++动态规划】2547. 拆分数组的最小代价|2019
  • 【论文投稿-第八届智能制造与自动化学术会议(IMA 2025)】HTML, CSS, JavaScript:三者的联系与区别
  • SOME/IP--协议英文原文讲解2
  • Python3 【函数】水平考试:精选试题和答案
  • MySQL数据导入与导出
  • MFC的绘制问题
  • p4:使用pytorch实现猴痘病识别
  • MySQL常用数据类型和表的操作
  • 【25美赛A题-F题全题目解析】2025年美国大学生数学建模竞赛(MCM/ICM)解题思路|完整代码论文集合
  • Linux 内核学习(4) --- devfreq 动态调频框架
  • 01学习预热篇(D6_正式踏入JVM深入学习前的铺垫)
  • An Attention Free Transformer论文参考文献
  • java 判断Date是上午还是下午
  • 基础IO(2)
  • 试用ChatGPT开发一个大语言模型聊天App