找回密码
 立即注册
查看: 19|回复: 0

【NOIP提高组2003】神经网络(洛谷P1038)题解:拓扑排序与动态规划的应用

[复制链接]
  • 打卡等级:即来则安
  • 打卡总天数:18
  • 打卡月天数:2
  • 打卡总奖励:194
  • 最近打卡:2025-08-14 15:12:44

22

主题

8

回帖

1万

积分

VIP

积分
10219
发表于 2025-7-17 15:41:47 | 显示全部楼层 |阅读模式
一、题目解读
2003年NOIP提高组中的“神经网络”题目(洛谷P1038)要求处理一个由神经元和带权有向边构成的网络。题目给定神经元的初始状态、阈值,以及神经元之间的连接关系,需要模拟信号传递过程,并输出最终状态。核心在于解决信号传递的顺序和条件判断,涉及到图论算法的应用。
二、解题思路
采用拓扑排序为核心思路,将神经网络抽象为有向无环(DAG)。关键在于处理节点的入度和状态更新:
1. 利用拓扑排序确定节点处理顺序,保证每个节点仅在其所有前驱节点处理后被访问;
2. 通过邻接表存储边信息,动态维护节点的入度和状态;
3. 节点传递信号的条件是其状态大于阈值,且传递权重影响后续节点状态。
三、解题步骤
1. 数据输入与初始化:读入神经元数量n、边数p,初始化神经元状态、阈值及邻接表;
2. 构建图结构:根据边信息更新入度,标记输出层节点;
3. 拓扑排序预处理:将入度为0的节点加入队列,并调整非输入层节点状态(减去阈值);
4. 拓扑排序迭代
○ 弹出队首节点,若状态≤0则不传递信号;
○ 否则向邻接点传递信号(状态加权更新),并递减其入度,若入度为0则加入队列;
5. 输出结果:最终状态即为输出层节点的状态。
四、代码与注释
  1. #include <iostream>
  2. #include <vector>
  3. #include <queue>
  4. using namespace std;

  5. // 定义神经元结构体
  6. struct Neuron {
  7. int c; // 当前状态
  8. int u; // 阈值
  9. int in_degree; // 入度
  10. bool is_output; // 是否为输出层神经元
  11. };

  12. int main() {
  13. int n, p;
  14. cin >> n >> p;

  15. vector<Neuron> neurons(n+1); // 神经元数组,从1开始编号
  16. vector<vector<pair<int, int>>> adj(n+1); // 邻接表,存储边和权重

  17. // 输入神经元初始状态和阈值
  18. for (int i = 1; i <= n; ++i) {
  19. cin >> neurons[i].c >> neurons[i].u;
  20. neurons[i].is_output = true; // 初始假设所有神经元都是输出层
  21. }

  22. // 输入边信息并构建邻接表
  23. for (int i = 0; i < p; ++i) {
  24. int from, to, w;
  25. cin >> from >> to >> w;
  26. adj[from].emplace_back(to, w);
  27. neurons[to].in_degree++; // 目标节点入度增加
  28. neurons[from].is_output = false; // 有出边的节点不是输出层
  29. }

  30. // 拓扑排序队列,初始化时加入所有入度为0的节点
  31. queue<int> q;
  32. for (int i = 1; i <= n; ++i) {
  33. if (neurons[i].in_degree == 0) {
  34. q.push(i);
  35. } else {
  36. // 非输入层神经元需要减去阈值
  37. neurons[i].c -= neurons[i].u;
  38. }
  39. }

  40. // 拓扑排序处理
  41. while (!q.empty()) {
  42. int current = q.front();
  43. q.pop();

  44. // 如果当前神经元状态<=0,不传递信号
  45. if (neurons[current].c <= 0) {
  46. for (auto &edge : adj[current]) {
  47. int next = edge.first;
  48. if (--neurons[next].in_degree == 0) {
  49. q.push(next);
  50. }
  51. }
  52. continue;
  53. }

  54. // 向所有邻接神经元传递信号
  55. for (auto &edge : adj[current]) {
  56. int next = edge.first;
  57. int weight = edge.second;
  58. neurons[next].c += weight * neurons[current].c;

  59. // 入度减为0时加入队列
  60. if (--neurons[next].in_degree == 0) {
  61. q.push(next);
  62. }
  63. }
  64. }

  65. // 收集输出层神经元结果
  66. bool has_output = false;
  67. for (int i = 1; i <= n; ++i) {
  68. if (neurons[i].is_output && neurons[i].c > 0) {
  69. cout << i << " " << neurons[i].c << endl;
  70. has_output = true;
  71. }
  72. }

  73. if (!has_output) {
  74. cout << "NULL" << endl;
  75. }

  76. return 0;
  77. }
复制代码


五、总结
该解法巧妙将神经网络转化为拓扑排序问题,通过动态规划思想维护节点状态,避免了复杂的递归深度优先搜索。关键在于利用入度控制节点处理顺序,确保信号传递的正确性。代码简洁高效,是解决此类图论问题的经典范例。
来源:CSP题解
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

SEO网站 ( 冀ICP备2025113422号-2 )|网站地图

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表