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;
}
相关推荐
算AI4 分钟前
人工智能+牙科:临床应用中的几个问题
人工智能·算法
hyshhhh2 小时前
【算法岗面试题】深度学习中如何防止过拟合?
网络·人工智能·深度学习·神经网络·算法·计算机视觉
杉之3 小时前
选择排序笔记
java·算法·排序算法
烂蜻蜓3 小时前
C 语言中的递归:概念、应用与实例解析
c语言·数据结构·算法
OYangxf3 小时前
图论----拓扑排序
算法·图论
我要昵称干什么3 小时前
基于S函数的simulink仿真
人工智能·算法
AndrewHZ3 小时前
【图像处理基石】什么是tone mapping?
图像处理·人工智能·算法·计算机视觉·hdr
念九_ysl3 小时前
基数排序算法解析与TypeScript实现
前端·算法·typescript·排序算法
守正出琦4 小时前
日期类的实现
数据结构·c++·算法
ChoSeitaku4 小时前
NO.63十六届蓝桥杯备战|基础算法-⼆分答案|木材加工|砍树|跳石头(C++)
c++·算法·蓝桥杯