2003年NOIP提高组神经网络(洛谷P1038):拓扑排序在生物神经网络中的应用

一. 问题理解
这道题目模拟了一个简化的生物神经网络模型。我们需要理解几个关键概念:
神经元:网络中的基本单元,每个神经元有当前状态(C)和阈值(U)
兴奋状态:当C > 0时,神经元处于兴奋状态,会向连接的神经元传递信号
层级结构:网络分为输入层、中间层和输出层,信息只能从输入层流向输出层
二. 算法选择
这个问题适合使用拓扑排序算法来解决,原因如下:
三. 实现解析
让我们分解代码的关键部分:
数据结构设计:
初始化阶段:
读取神经元初始状态和阈值
构建邻接表时,同时更新目标神经元的入度
标记有出边的神经元为非输出层
拓扑排序处理:
初始化队列时加入所有入度为0的神经元(输入层)
对于每个处理的神经元:
如果状态≤0,不传递信号,仅减少邻接神经元入度
如果状态>0,计算对邻接神经元的影响
邻接神经元入度减为0时加入队列
输出处理:
遍历所有神经元,输出状态>0的输出层神经元
如果没有符合条件的输出,输出"NULL"
四. 关键点说明
阈值处理:非输入层神经元初始状态为0,需要先减去阈值U
信号传递:只有当神经元兴奋(C>0)时才传递信号
拓扑顺序:确保每个神经元在被处理前,所有输入信号都已计算完成
五. 完整代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// 定义神经元结构体
struct Neuron {
int c; // 当前状态
int u; // 阈值
int in_degree; // 入度
bool is_output; // 是否为输出层神经元
};
int main() {
int n, p;
cin >> n >> p;
vector<Neuron> neurons(n+1); // 神经元数组,从1开始编号
vector<vector<pair<int, int>>> adj(n+1); // 邻接表,存储边和权重
// 输入神经元初始状态和阈值
for (int i = 1; i <= n; ++i) {
cin >> neurons[i].c >> neurons[i].u;
neurons[i].is_output = true; // 初始假设所有神经元都是输出层
}
// 输入边信息并构建邻接表
for (int i = 0; i < p; ++i) {
int from, to, w;
cin >> from >> to >> w;
adj[from].emplace_back(to, w);
neurons[to].in_degree++; // 目标节点入度增加
neurons[from].is_output = false; // 有出边的节点不是输出层
}
// 拓扑排序队列,初始化时加入所有入度为0的节点
queue<int> q;
for (int i = 1; i <= n; ++i) {
if (neurons[i].in_degree == 0) {
q.push(i);
} else {
// 非输入层神经元需要减去阈值
neurons[i].c -= neurons[i].u;
}
}
// 拓扑排序处理
while (!q.empty()) {
int current = q.front();
q.pop();
// 如果当前神经元状态<=0,不传递信号
if (neurons[current].c <= 0) {
for (auto &edge : adj[current]) {
int next = edge.first;
if (--neurons[next].in_degree == 0) {
q.push(next);
}
}
continue;
}
// 向所有邻接神经元传递信号
for (auto &edge : adj[current]) {
int next = edge.first;
int weight = edge.second;
neurons[next].c += weight * neurons[current].c;
// 入度减为0时加入队列
if (--neurons[next].in_degree == 0) {
q.push(next);
}
}
}
// 收集输出层神经元结果
bool has_output = false;
for (int i = 1; i <= n; ++i) {
if (neurons[i].is_output && neurons[i].c > 0) {
cout << i << " " << neurons[i].c << endl;
has_output = true;
}
}
if (!has_output) {
cout << "NULL" << endl;
}
return 0;
}六. 学习价值
通过这道题目,我们可以学习:
如何将实际问题抽象为图论模型
拓扑排序的应用场景
生物神经网络的基本原理
如何设计合适的数据结构来解决问题
七. 扩展思考
如果网络中存在环会怎样?如何检测?
如何优化算法以处理更大规模的网络?
如何修改模型使其更接近真实的生物神经网络?
这道题目很好地结合了图论和生物神经网络的知识,通过代码实现加深了对这两个领域的理解。
原创内容 转载请注明出处
