P1038 [NOIP 2003 提高组] 神经网络

题目描述

在兰兰的模型中,神经网络就是一张有向图,图中的节点称为神经元,而且两个神经元之间至多有一条边相连,下图是一个神经元的例子:

神经元(编号为 i)

图中,X1​∼X3​ 是信息输入渠道,Y1​∼Y2​ 是信息输出渠道,Ci​ 表示神经元目前的状态,Ui​ 是阈值,可视为神经元的一个内在参数。

神经元按一定的顺序排列,构成整个神经网络。在兰兰的模型之中,神经网络中的神经元分为几层;称为输入层、输出层,和若干个中间层。每层神经元只向下一层的神经元输出信息,只从上一层神经元接受信息。下图是一个简单的三层神经网络的例子。

兰兰规定,Ci​ 服从公式:​ (其中 n 是网络中所有神经元的数目)

公式中的 Wji​(可能为负值)表示连接 j 号神经元和 i 号神经元的边的权值。当 Ci​ 大于 0 时,该神经元处于兴奋状态,否则就处于平静状态。当神经元处于兴奋状态时,下一秒它会向其他神经元传送信号,信号的强度为 Ci​。

如此.在输入层神经元被激发之后,整个网络系统就在信息传输的推动下进行运作。现在,给定一个神经网络,及当前输入层神经元的状态(Ci​),要求你的程序运算出最后网络输出层的状态。

输入格式

输入文件第一行是两个整数 n(1≤n≤100)和 p。接下来 n 行,每行 2 个整数,第 i+1 行是神经元 i 最初状态和其阈值(Ui​),非输入层的神经元开始时状态必然为 0。再下面 p 行,每行有两个整数 i,j 及一个整数 Wij​,表示连接神经元 i,j 的边权值为 Wij​。

输出格式

输出文件包含若干行,每行有 2 个整数,分别对应一个神经元的编号,及其最后的状态,2 个整数间以空格分隔。仅输出最后状态大于 0 的输出层神经元状态,并且按照编号由小到大顺序输出。

若输出层的神经元最后状态均小于等于 0,则输出 NULL

输入输出样例

输入 #1

复制代码
5 6
1 0
1 0
0 1
0 1
0 1
1 3 1
1 4 1
1 5 1
2 3 1
2 4 1
2 5 1

输出 #1

复制代码
3 1
4 1
5 1

可以想到通过入度和出度确定输入输出层,随后对剩下的神经元进行分层排序,最后按照层序更新神经元状态,代码如下:

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;

struct Edge {
    int node;// 前驱/后继神经元c
    int weight;// 边权
};

int main() {
    int n, p;
    cin >> n >> p;
    vector<int> c(n + 1);// 神经元i的初始状态
    vector<int> u(n + 1);// 神经元i的阈值
    vector<vector<Edge>> prev(n + 1);// 神经元i的前驱神经元
    vector<vector<Edge>> next(n + 1);// 神经元i的后继神经元

    for (int i = 1; i <= n; ++i) {
        int init, threshold;
        cin >> init >> threshold;
        c[i] = init;
        u[i] = threshold;
    }

    for (int i = 0; i < p; ++i) {
        int from, to, weight;
        cin >> from >> to >> weight;
        prev[to].push_back({from, weight});
        next[from].push_back({to, weight});
    }

    vector<int> layer(n + 1, 0);// 神经元i的层数
    bool changed = true;
    while (changed) {// 不断更新层数
        changed = false;
        for (int i = 1;i<=n;i++) {
            if (prev[i].empty()) {
                if (layer[i]!=0) {
                    layer[i] = 0;
                    changed = true;
                }
                continue;
            }
            int max_prev = 0;
            for (auto edge : prev[i]) {
                if (layer[edge.node]>max_prev) {
                    max_prev = layer[edge.node];
                }
            }
            int new_layer = max_prev + 1;
            if (new_layer != layer[i]) {
                layer[i] = new_layer;
                changed = true;
            }
        }
    }

    vector<pair<int, int>> nodes;// 神经元i的层数和编号
    for (int i = 1;i<=n;i++) {
        nodes.push_back({layer[i], i});
    }
    sort(nodes.begin(), nodes.end());

    for (auto node : nodes) {// 从前往后逐层更新神经元状态
        int i = node.second;
        if (layer[i] == 0) {
            continue;
        }
        int sum = 0;
        for (auto edge : prev[i]) {
            if (c[edge.node]>0) {
                sum += edge.weight * c[edge.node];
            }
        }
        c[i] = sum - u[i];
    }

    vector<int> output_nodes;// 找出输出神经元
    for (int i = 1;i<=n;i++) {
        if (next[i].empty()) {
            output_nodes.push_back(i);
        }
    }
    sort(output_nodes.begin(), output_nodes.end());

    vector<int> result;
    for (int node : output_nodes) {// 过滤状态为正的神经元
        if (c[node] > 0) {
            result.push_back(node);
        }
    }

    if (result.empty()) {
        cout << "NULL" << endl;
    } else {
        for (auto p : result) {
            cout << p << " " << c[p] << endl;
        }
    }
    return 0;
}
相关推荐
mit6.8241 小时前
[Lc_week] 447 | 155 | Q1 | hash | pair {}调用
算法·leetcode·哈希算法·散列表
jerry6092 小时前
优先队列、堆笔记(算法第四版)
java·笔记·算法
勤劳的牛马3 小时前
📚 小白学算法 | 每日一题 | 算法实战:加1!
算法
Epiphany.5563 小时前
基于c++的LCA倍增法实现
c++·算法·深度优先
一只码代码的章鱼3 小时前
学习笔记2(Lombok+算法)
笔记·学习·算法
jerry6094 小时前
c++流对象
开发语言·c++·算法
2301_817031655 小时前
C语言-- 深入理解指针(4)
c语言·开发语言·算法
·醉挽清风·5 小时前
学习笔记—双指针算法—移动零
c++·笔记·学习·算法
几点才到啊5 小时前
使用 malloc 函数模拟开辟一个 3x5 的整型二维数组
数据结构·算法
编程绿豆侠5 小时前
力扣HOT100之链表:23. 合并 K 个升序链表
算法·leetcode·链表