Loading

P1038 题解

P1038 [NOIP2003 提高组] 神经网络

题意

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

对于第 \(i\) 个神经元来说,\(C_i\) 表示神经元目前的状态,\(U_i\) 是阈值,可视为神经元的一个内在参数。

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

兰兰规定,\(C_i\) 服从公式:

\[C_i = \left ( \sum \limits _ {(j,i) \in E} W_{j,i} \times C_{j} \right ) - U_{i} \]

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

现在,给定一个神经网络,及当前输入层神经元的状态(\(C_i\)),要求你的程序运算出最后网络输出层的状态。

思路

在题目中,有这么一个公式:

\[C_i = \left ( \sum \limits _ {(j,i) \in E} W_{j,i} \times C_{j} \right ) - U_{i} \]

通过这个公式,我们可以知道,如果想要求解 \(C_i\),就必须先求解出所有的 \(C_j\),并且 \(j\)\(i\) 连了一条边。

那么,这不就是一个标准的拓扑排序 + 递推吗?

先拓扑排序求出拓扑序,再根据拓扑序求出所有 \(C_i\),最后输出即可。

但是,要注意,这个题目有三个坑点:

  1. 在公式下面,有这么一段话:
  • \(C_i\) 大于 \(0\) 时,该神经元处于兴奋状态,否则就处于平静状态。当神经元处于兴奋状态时,下一秒它会向其他神经元传送信号,信号的强度为 \(C_i\)

  • 这里说明,这个公式应该再增加一个条件,所有会被计算的 \(C_j\) 必须大于 0,也就是

\[C_i = \left ( \sum \limits _ {(j,i) \in E, C_j > 0} W_{j,i} \times C_{j} \right ) - U_{i} \]

  1. 输入层的神经元是不能减去阈值的!!!

  2. 题目中有这么一句话:

非输入层的神经元开始时状态必然为 0

但这不代表输入层的神经元开始时状态一定不为 \(0\),这是一个逻辑问题,所以不能直接按照 \(C_i\) 来判断是否为输入层。

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 110;

int n, p, c[N], u[N], s[N];

struct Node {
  int v, w;
};

vector<Node> g[N];
queue<int> que;

void bfs() {  // 求拓扑序
  for (int i = 1; i <= n; i++) {
    if (!s[i]) {
      que.push(i);
    }
  }
  while (!que.empty()) {
    int tmp = que.front();
    que.pop();
    for (auto v : g[tmp]) {
      s[v.v]--, c[v.v] += (c[tmp] > 0) * c[tmp] * v.w;  // 顺便求出 c[i]
      if (!s[v.v]) {
        que.push(v.v);
      }
    }
  }
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n >> p;
  for (int i = 1; i <= n; i++) {
    cin >> c[i] >> u[i];
  }
  while (p--) {
    int x, y, w;
    cin >> x >> y >> w;
    g[x].push_back({y, w}), s[y]++;
  }
  for (int i = 1; i <= n; i++) {
    if (s[i]) {
      c[i] -= u[i];  // 先将所有 u[i] 都处理好
    }
  }
  bfs();
  bool flag = 0;
  for (int i = 1; i <= n; i++) {
    if (g[i].empty() && c[i] > 0) {  // 输出答案
      cout << i << ' ' << c[i] << '\n';
      flag = 1;
    }
  }
  if (!flag) {
    cout << "NULL";
  }
  return 0;
}
posted @ 2023-03-02 22:49  chengning0909  阅读(75)  评论(0编辑  收藏  举报