首页 > 其他分享 >洛谷刷题日记12||图的遍历

洛谷刷题日记12||图的遍历

时间:2024-11-27 13:31:15浏览次数:12  
标签:12 洛谷 int 队列 任务 奶牛 节点 dp 刷题

反向建边 + dfs

按题目来每次考虑每个点可以到达点编号最大的点,不如考虑较大的点可以反向到达哪些点

循环从N到1,则每个点i能访问到的结点的A值都是i

每个点访问一次,这个A值就是最优的,因为之后如果再访问到这个结点那么答案肯定没当前大了

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

#define MAXL 100010 // 最大节点数

int N, M; // 节点数和边数
int A[MAXL]; // 结果数组,A[x] 表示从节点 x 出发能到达的编号最大的点
vector<int> G[MAXL]; // 邻接表,用于存储图

// 深度优先搜索函数
void dfs(int x, int d) {
    if (A[x]) return; // 如果节点 x 已经访问过,直接返回
    A[x] = d; // 更新节点 x 的结果为 d,表示从 d 出发可以到达 x
    for (int i = 0; i < G[x].size(); i++) { // 遍历节点 x 的所有邻接点  反向
        dfs(G[x][i], d); // 递归处理邻接点
    }
}

int main() {
    int u, v; // 边的两个端点
    scanf("%d%d", &N, &M); // 输入节点数 N 和边数 M

    // 读入边并构建反向图
    for (int i = 1; i <= M; i++) {
        scanf("%d%d", &u, &v); // 输入边 (u, v)
        G[v].push_back(u); // 反向建边,将 v 指向 u
    }

    // 从编号最大的节点依次进行 DFS
    for (int i = N; i > 0; i--) {
        dfs(i, i); // 从节点 i 出发,更新所有能到达的节点
    }

    // 输出结果
    for (int i = 1; i <= N; i++) {
        printf("%d ", A[i]); // 输出从 1 到 N 的结果
    }
    printf("\n"); // 换行

    return 0;
}



 这是一个经典的 拓扑排序 + 动态规划 问题。可以将每个杂务视为一个节点,准备工作视为有向边,然后通过拓扑排序确定每个节点的完成顺序,最后使用动态规划求出完成所有工作的最短时间。以下是用 C++ 实现该问题的完整代码。

 

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

// 定义最大节点数
const int MAXN = 10000;

vector<int> adj[MAXN + 1]; // 邻接表存储依赖关系,adj[i] 表示 i 的所有后继任务
int in_degree[MAXN + 1];   // 每个节点的入度,表示还有多少依赖任务未完成
int time_needed[MAXN + 1]; // 每个任务所需的完成时间
int dp[MAXN + 1];          // dp[i] 表示完成到第 i 个任务的最短时间

int main() {
    int n;
    cin >> n; // 输入任务数

    // 输入每个任务的信息
    for (int i = 1; i <= n; ++i) {
        int task, time, dep;
        cin >> task >> time; // 任务编号和所需时间
        time_needed[task] = time; // 保存任务所需时间

        // 输入当前任务的所有依赖任务
        while (cin >> dep && dep != 0) {
            adj[dep].push_back(task); // 从依赖任务到当前任务的有向边
            ++in_degree[task]; // 当前任务的入度加 1
        }
    }

    // 使用队列进行拓扑排序
    queue<int> q;
    for (int i = 1; i <= n; ++i) {
        // 将所有入度为 0 的任务加入队列(可以直接开始的任务)
        if (in_degree[i] == 0) {
            q.push(i);
            dp[i] = time_needed[i]; // 初始任务的最短时间就是自身所需时间
        }
    }

    // 拓扑排序处理任务
    while (!q.empty()) {
        int cur = q.front(); // 当前处理的任务
        q.pop();

        // 遍历当前任务的所有后继任务
        for (int next : adj[cur]) {
            // 更新后继任务的最短完成时间
            dp[next] = max(dp[next], dp[cur] + time_needed[next]);
            // 将后继任务的入度减 1
            if (--in_degree[next] == 0) {
                // 如果后继任务的入度变为 0,加入队列
                q.push(next);
            }
        }
    }

    // 最后计算所有任务的最短完成时间
    int result = 0;
    for (int i = 1; i <= n; ++i) {
        result = max(result, dp[i]); // 找到最大的 dp 值
    }

    cout << result << endl; // 输出结果

    return 0;
}

关键逻辑详解

  1. 任务依赖建图

    • 使用邻接表 adj 存储任务的依赖关系,每个任务指向它的后续任务。
    • 入度数组 in_degree 记录每个任务的依赖任务数。
  2. 拓扑排序

    • 使用队列处理入度为 0 的任务,表示当前可以直接完成的任务。
    • 完成一个任务后,将其后续任务的入度减 1;如果后续任务的入度变为 0,则加入队列。
  3. 动态规划更新最短完成时间

    • dp[i] 表示完成任务 i 的最短时间。
    • 更新公式:dp[next] = max(dp[next], dp[cur] + time_needed[next]),即后续任务的完成时间至少是当前任务完成时间加上后续任务所需时间。
  4. 结果计算

    • 最后取所有 dp[i] 的最大值,表示完成所有任务的最短时间。

 样例输入

7
1 5 0
2 2 1 0
3 3 2 0
4 6 1 0
5 1 2 4 0
6 8 2 4 0
7 4 3 5 6 0

 

算法逻辑

第 1 步:初始化队列

将入度为 0 的任务加入队列(这些任务可以直接开始完成):

初始队列:[1]
初始 dp:dp[1] = 5 (任务 1 自身时间)


第 2 步:拓扑排序和动态规划更新

依次从队列中取出任务,更新其后续任务的完成时间。

处理过程:

  1. 取出任务 1

    • 后续任务:2, 4
    • 更新后续任务的 dp:
      • dp[2] = max(dp[2], dp[1] + time_needed[2]) = 5 + 2 = 7
      • dp[4] = max(dp[4], dp[1] + time_needed[4]) = 5 + 6 = 11
    • 入度更新:
      • in_degree[2] = 0 → 加入队列
      • in_degree[4] = 0 → 加入队列

    队列:[2, 4]

  2. 取出任务 2

    • 后续任务:3, 5, 6
    • 更新后续任务的 dp:
      • dp[3] = max(dp[3], dp[2] + time_needed[3]) = 7 + 3 = 10
      • dp[5] = max(dp[5], dp[2] + time_needed[5]) = 7 + 1 = 8
      • dp[6] = max(dp[6], dp[2] + time_needed[6]) = 7 + 8 = 15
    • 入度更新:
      • in_degree[3] = 0 → 加入队列
      • in_degree[5] = 1
      • in_degree[6] = 1

    队列:[4, 3]

  3. 取出任务 4

    • 后续任务:5, 6
    • 更新后续任务的 dp:
      • dp[5] = max(dp[5], dp[4] + time_needed[5]) = 11 + 1 = 12
      • dp[6] = max(dp[6], dp[4] + time_needed[6]) = 11 + 8 = 19
    • 入度更新:
      • in_degree[5] = 0 → 加入队列
      • in_degree[6] = 0 → 加入队列

    队列:[3, 5, 6]

  4. 后面的类似




 

解题思路

  1. 反向建图:将所有有向边反向,变成从目标节点指向源节点的边。这样可以方便地从每个奶牛所在的牧场出发,找到所有可以到达的节点。

  2. 多源 BFS:对于每头奶牛所在的牧场,利用 BFS 或 DFS 遍历所有可到达的节点,同时记录每个节点被多少头奶牛访问过。

  3. 统计结果:如果某个节点被所有奶牛访问过(即计数等于奶牛数量 K),则该节点为可行的聚集地点。

 

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>

using namespace std;

const int MAXN = 1000; // 最大牧场数
const int MAXK = 100;  // 最大奶牛数

// 反向图的邻接表
vector<int> reverse_graph[MAXN + 1];
// 每个牧场被访问的奶牛数量
int visit_count[MAXN + 1];

// 使用 BFS 遍历从某个起始点可以到达的所有节点
void bfs(int start) {
    // 记录当前奶牛访问时是否已经到达某节点
    bool visited[MAXN + 1] = {false};
    queue<int> q;

    // 初始化 BFS 队列
    q.push(start);
    visited[start] = true;

    // 开始 BFS
    while (!q.empty()) {
        int cur = q.front();
        q.pop();

        // 更新当前节点被访问的次数
        visit_count[cur]++;

        // 遍历反向图中从当前节点可以到达的所有邻居节点
        for (int next : reverse_graph[cur]) {
            if (!visited[next]) { // 如果邻居节点未被访问
                visited[next] = true;
                q.push(next);
            }
        }
    }
}

int main() {
    int K, N, M; // K:奶牛数, N:牧场数, M:路径数
    cin >> K >> N >> M;

    vector<int> cow_start(K); // 每头奶牛所在的初始牧场

    // 输入奶牛的初始位置
    for (int i = 0; i < K; ++i) {
        cin >> cow_start[i];
    }

    // 输入路径,并反向建图
    for (int i = 0; i < M; ++i) {
        int A, B;
        cin >> A >> B;
        reverse_graph[B].push_back(A); // 反向建图:从 B 指向 A
    }

    // 初始化访问计数
    memset(visit_count, 0, sizeof(visit_count));

    // 对每头奶牛的初始牧场执行 BFS
    for (int cow : cow_start) {
        bfs(cow);
    }

    // 统计被所有奶牛访问的牧场数
    int result = 0;
    for (int i = 1; i <= N; ++i) { // 遍历所有牧场
        if (visit_count[i] == K) { // 如果某牧场被 K 头奶牛访问
            result++;
        }
    }

    // 输出结果
    cout << result << endl;

    return 0;
}

输入

2 4 4
2
3
1 2
1 4
2 3
3 4

 

数据解读
  • 奶牛初始位置:

    • 奶牛 1 在牧场 2
    • 奶牛 2 在牧场 3
  • 路径(反向建图):

    • 原始路径 1→2,1→4,2→3,3→4
    • 反向图:
      • 2→1
      • 4→1
      • 3→2
      • 4→3

运行过程
  1. 奶牛 1 的 BFS:

    • 从节点 2 出发,访问节点:2 → 3 → 4。
  2. 奶牛 2 的 BFS:

    • 从节点 3 出发,访问节点:3 → 4。
  3. 统计结果:从每头牛开始遍历,把他经过的牧场访问+1

    • 节点 3 和节点 4 被两头奶牛访问,因此是可行的聚集地点。

标签:12,洛谷,int,队列,任务,奶牛,节点,dp,刷题
From: https://blog.csdn.net/ke_wu/article/details/144043565

相关文章

  • 库卡机器人KR120示教器日常保养技巧
         库卡机器人KR120是一款高效、精准的工业机器人,广泛应用于各个领域。然而,要确保其长期稳定运行,日常的保养和维护至关重要。下面,我们将为您介绍库卡机器人KR120示教器的日常保养技巧。      一、定期清洁      示教器作为与机器人交互的重......
  • deepin 技术双周报丨Treeland支持截图录屏功能、适配 wlroots 0.18 版本,6.12 内核完成
    第六期deepin技术双周报已出炉,我们会简单列出deepin各个小组在过去两周的相关工作进展,也会阐述未来两周的大致规划,一起来看!DDE针对deepin23的缺陷修复与deepin25的需求开发在同步稳步进行。具体进展与计划如下:进展:a.  对剪切板、DDE会话组件、DDEPolkit组件......
  • 12万字 java 面经总结-面试篇
    *基础篇**1**、**Java**语言有哪些特点*1、简单易学、有丰富的类库2、面向对象(Java最重要的特性,让程序耦合度更低,内聚性更高)3、与平台无关性(JVM是Java跨平台使用的根本)4、可靠安全5、支持多线程*2**、面向对象和面向过程的区别**面向过程*:是分析解决问题的步骤,然......
  • 问EBS R12中怎样实现输出格式是多sheet页excel报表,不用excel模板实现,而是在sqlplus中
    https://www.itpub.net/thread-2094848-1-1.html 来源 手工创建一个EXCEL,放一些数据进去,然后另存为xml表格,用notepad打开看看,里面有代码。把代码用SQL拼接起来。<?xmlversion="1.0"?><?mso-applicationprogid="Excel.Sheet"?><Workbookxmlns="urn:schemas-m......
  • HCIA基础02课后习题1126
    问题1:普通用户:        例如有一个名为user1的普通用户,当user1登录系统后在命令行中输入cd~,就会进入到/home/user1目录(通常情况下,普通用户的主目录在/home目录下,目录名和用户名相同)。root用户(超级用户):        当以root身份登录系统,在命令行输......
  • 【IEEE独立出版 | 厦门大学主办】第四届人工智能、机器人和通信国际会议(ICAIRC 2024,12
    第四届人工智能、机器人和通信国际会议(ICAIRC2024)20244thInternationalConferenceonArtificialIntelligence,Robotics,andCommunication重要信息会议官网:www.icairc.net三轮截稿时间:2024年11月30日23:59录用通知时间:投稿后1周左右会议检索:IEEE......
  • SS241126C. 树(tree)
    SS241126C.树(tree)题意给你一个以\(1\)为根的树,每个点有点权\(v_i\)。设这棵树的点集为\(V\),一个合法的子集\(V'\subseteqV\),满足存在\(p\inV'\),使得\(V'\)中任意两点的LCA都是\(p\)。把\(V\)分成若干个\(V'\)称为一种划分方案,一种划分方案\(\{V'\}\)的......
  • 洛谷P1478(洛谷P1223)
    因为这两题相似,把它们放在一个博客里面发了陶陶摘苹果(升级版)-洛谷陶陶摘苹果(升级版)题目描述又是一年秋季时,陶陶家的苹果树结了n个果子。陶陶又跑去摘苹果,这次他有一个a公分的椅子。当他手够不着时,他会站到椅子上再试试。这次与NOIp2005普及组第一题不同的是:陶陶之前搬......
  • 洛谷P8833
    [传智杯#3决赛]课程-洛谷[传智杯#3决赛]课程题目背景disangan233喜欢数数,于是他想让你帮他回答一个问题。题目描述传智专修学院提供A,B两个课程,分别有n,m个学生报名。报名A的学生的编号为a_n,报名B 的学生的编号为b_m,求有多少个学生同时报名了两个课程。对......
  • SSM机床智能制造预警管理x12n4(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义机床作为制造业的核心设备,其智能化水平直接影响生产效率和产品质量。然而,机床运行中常因多种因素出现故障,影响生产进度和设备寿......