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

拓扑排序+dp

小明要去一个国家旅游。这个国家有 N 个城市,编号为 1 至 N,并且有 M 条道路连接着,小明准备从其中一个城市出发,并只往东走到城市 i 停止。

所以他就需要选择最先到达的城市,并制定一条路线以城市 i 为终点,使得线路上除了第一个城市,每个城市都在路线前一个城市东面,并且满足这个前提下还希望游览的城市尽量多。

现在,你只知道每一条道路所连接的两个城市的相对位置关系,但并不知道所有城市具体的位置。现在对于所有的 i,都需要你为小明制定一条路线,并求出以城市 i 为终点最多能够游览多少个城市。

输入格式

第一行为两个正整数 N,M。

接下来 M 行,每行两个正整数 x,y,表示了有一条连接城市 x 与城市 y 的道路,保证了城市 x 在城市 y 西面。

输出格式

N 行,第 i 行包含一个正整数,表示以第 i 个城市为终点最多能游览多少个城市。

输入输出样例

输入 #1复制

5 6
1 2
1 3
2 3
2 4
3 4
2 5

输出 #1复制

1
2
3
4
3

说明/提示

均选择从城市 1 出发可以得到以上答案。

  • 对于 20% 的数据,1≤N≤100;
  • 对于 60% 的数据,1≤N≤1000;
  • 对于 100% 的数据,1≤N≤100000,1≤M≤200000。

//拓扑排序排的就是先后顺序

#include <iostream>

#include <vector>

#include <queue>

using namespace std;

int main() {

    ios::sync_with_stdio(false);

    cin.tie(nullptr);

    int N, M;

    cin >> N >> M;

    vector<vector<int>> adj(N + 1);  // 邻接表

    vector<int> in_degree(N + 1, 0); // 入度

    vector<int> dp(N + 1, 1);        // dp[i] 表示以 i 为终点的最长路径

    for (int i = 0; i < M; ++i) {

        int x, y;

        cin >> x >> y;

        adj[x].push_back(y);  // x -> y

        in_degree[y]++;       // y 的入度 +1

    }

    // 拓扑排序(BFS)

    queue<int> q;

    for (int i = 1; i <= N; ++i) {

        if (in_degree[i] == 0) {

            q.push(i);  // 入度为 0 的节点入队

        }

    }

    while (!q.empty()) {

        int u = q.front();

        q.pop();

        for (int v : adj[u]) {

            // 更新 dp[v]

            if (dp[v] < dp[u] + 1) {

                dp[v] = dp[u] + 1;

            }

            // 减少入度,若为 0 则入队

            if (--in_degree[v] == 0) {

                q.push(v);

            }

        }

    }

    // 输出结果

    for (int i = 1; i <= N; ++i) {

        cout << dp[i] << "\n";

    }

    return 0;

}

for (int v : adj[u]) 是 C++11 引入的范围 for 循环(Range-based for loop),用于遍历容器(如数组、vectorlist 等)中的元素。


具体含义

  • adj[u] 是一个 vector<int>,存储了所有从节点 u 出发能直接到达的节点(即 u 的邻接节点)。

  • for (int v : adj[u]) 表示:

    • 依次取出 adj[u] 中的每一个元素,赋值给变量 v

    • 然后执行循环体内的代码(如 dp[v] = max(dp[v], dp[u] + 1))。


等价写法

如果用传统的 for 循环,可以写成:

cpp

复制

下载

for (int i = 0; i < adj[u].size(); ++i) {int v = adj[u][i];// 循环体
}

或使用迭代器:

cpp

复制

下载

for (vector<int>::iterator it = adj[u].begin(); it != adj[u].end(); ++it) {int v = *it;// 循环体
}

但 范围 for 循环更简洁,且不易出错(无需手动管理索引或迭代器)。


在该代码中的作用

这段代码是 拓扑排序 + DP 的核心部分

cpp

复制

下载

for (int v : adj[u]) {// 更新 dp[v]if (dp[v] < dp[u] + 1) {dp[v] = dp[u] + 1;}// 减少入度,若为 0 则入队if (--in_degree[v] == 0) {q.push(v);}
}
  • v 是 u 的直接后继节点(即 u → v 是一条边)。

  • 更新 dp[v]:如果从 u 走到 v 能获得更长的路径(dp[u] + 1),则更新 dp[v]

  • 减少 v 的入度:如果 v 的入度变为 0,加入队列(拓扑排序的标准操作)。


示例说明

假设 adj[2] = {3, 4, 5}(即节点 2 有 3 条边:2→32→42→5),则:

cpp

复制

下载

for (int v : adj[2]) {// v 依次为 3、4、5// 对每个 v 执行循环体
}

相当于:

cpp

复制

下载

int v = 3; // 第一次循环
dp[3] = max(dp[3], dp[2] + 1);
if (--in_degree[3] == 0) q.push(3);v = 4;    // 第二次循环
dp[4] = max(dp[4], dp[2] + 1);
if (--in_degree[4] == 0) q.push(4);v = 5;    // 第三次循环
dp[5] = max(dp[5], dp[2] + 1);
if (--in_degree[5] == 0) q.push(5);

总结

语法含义
for (int v : adj[u])遍历 u 的所有邻接节点 v
适用场景需要遍历容器(如 vectorarray)的所有元素
优势代码简洁,无需手动管理索引或迭代器

这种写法在 图论算法(如 DFS、BFS、拓扑排序)中非常常见,建议熟练掌握!

http://www.xdnf.cn/news/5845.html

相关文章:

  • STM32-DMA数据转运(8)
  • 直接在Excel中用Python Matplotlib/Seaborn/Plotly......
  • Linux 内核网络协议栈:从 Socket 类型到协议注册的深度解析
  • 思迈特软件携手天阳科技,打造ChatBI金融智能分析新标杆
  • 适应性神经树:当深度学习遇上决策树的“生长法则”
  • Spring Boot 整合 Redis 实战
  • MySQL 事务(二)
  • 在 Qt Creator 中为 QDockWidget 设置隐藏和显示按钮
  • 中电金信参编的国家标准《信息技术 中间件 消息中间件技术要求》正式发布
  • 【爬虫】DrissionPage-1
  • 【TDengine源码阅读】#if defined(__APPLE__)
  • (C语言)超市管理系统(测试版)(指针)(数据结构)(二进制文件读写)
  • Android支持离线功能的复杂业务场景(如编辑、同步):设计数据同步策略的解决方案
  • 基于大模型的腰椎管狭窄术前、术中、术后全流程预测与治疗方案研究报告
  • 数据服务包括哪些内容?一文讲清数据服务模块的主要功能!
  • 【HarmonyOs鸿蒙】七种传参方式
  • IoTDB集群的一键启停功能详解
  • 裸机开发的核心技术:轮询、中断与DMA
  • PowerShell 实现 conda 懒加载
  • MUSE Pi Pro 编译kernel内核及创建自动化脚本进行环境配置
  • 什么是IoT长连接服务?
  • 最终一致性和强一致性
  • Datawhale 5月coze-ai-assistant 笔记1
  • 免费实用的远程办公方案​
  • Spark的缓存
  • 麦肯锡110页PPT企业组织效能提升调研与诊断分析指南
  • 从0到1上手Kafka:开启分布式消息处理之旅
  • ES6中的解构
  • 【SpringBoot】集成kafka之生产者、消费者、幂等性处理和消息积压
  • c语言第一个小游戏:贪吃蛇小游戏08(贪吃蛇完结)