洛谷P1038 神经网络(bfs,模拟,拓扑)
题目背景
人工神经网络(Artificial Neural NetworkArtificialNeuralNetwork)是一种新兴的具有自我学习能力的计算系统,在模式识别、函数逼近及贷款风险评估等诸多领域有广泛的应用。对神经网络的研究一直是当今的热门方向,兰兰同学在自学了一本神经网络的入门书籍后,提出了一个简化模型,他希望你能帮助他用程序检验这个神经网络模型的实用性。
题目描述
在兰兰的模型中,神经网络就是一张有向图,图中的节点称为神经元,而且两个神经元之间至多有一条边相连,下图是一个神经元的例子:
神经元〔编号为11)
图中,X_1-X_3X1−X3是信息输入渠道,Y_1-Y_2Y1−Y2是信息输出渠道,C_1C1表示神经元目前的状态,U_iUi是阈值,可视为神经元的一个内在参数。
神经元按一定的顺序排列,构成整个神经网络。在兰兰的模型之中,神经网络中的神经元分为几层;称为输入层、输出层,和若干个中间层。每层神经元只向下一层的神经元输出信息,只从上一层神经元接受信息。下图是一个简单的三层神经网络的例子。
兰兰规定,C_iCi服从公式:(其中nn是网络中所有神经元的数目)
C_i=\sum_{j,i \in E} W_{ji}C_{j}-U{i}Ci=∑j,i∈EWjiCj−Ui
公式中的W_{ji}Wji(可能为负值)表示连接jj号神经元和ii号神经元的边的权值。当 C_iCi大于00时,该神经元处于兴奋状态,否则就处于平静状态。当神经元处于兴奋状态时,下一秒它会向其他神经元传送信号,信号的强度为C_iCi。
如此.在输入层神经元被激发之后,整个网络系统就在信息传输的推动下进行运作。现在,给定一个神经网络,及当前输入层神经元的状态(C_iCi),要求你的程序运算出最后网络输出层的状态。
输入输出格式
输入格式:
输入文件第一行是两个整数n(1 \le n \le 100)n(1≤n≤100)和pp。接下来nn行,每行22个整数,第i+1i+1行是神经元ii最初状态和其阈值(U_iUi),非输入层的神经元开始时状态必然为00。再下面PP行,每行由22个整数i,ji,j及11个整数W_{ij}Wij,表示连接神经元i,ji,j的边权值为W_{ij}Wij。
输出格式:
输出文件包含若干行,每行有22个整数,分别对应一个神经元的编号,及其最后的状态,22个整数间以空格分隔。仅输出最后状态大于00的输出层神经元状态,并且按照编号由小到大顺序输出。
若输出层的神经元最后状态均为 00,则输出 “NULL”。
输入输出样例
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
3 1 4 1 5 1
题目类型:
模拟,BFS,拓扑排序
思路:
这道题主要模拟神经网络的传播,依次更新每一层的结点,类似于BFS。首先将通过vector数组v[i]记录第i个结点相连的后一层结点。然后先将第一层的点放入队列,之后每次从队列中取出一个点,更新与这个点相连的后一层的所有结点的C。由于点更新完成后要减去U,那么如何判断某个点将不再被更新了呢?这就需要用到拓扑排序的知识。初始时要记录每一个点的入度,用上一层的i结点更新下一次的j结点以后,j结点的入度就减1,但j结点的入度为0是,即代表j结点将不再会被更新,于是就可以减去U。一直更新队列,按层遍历完整个网络以后,最后一层的状态就随之得到了。
#include<bits/stdc++.h> #define ll long long using namespace std; struct node{ int to;//与哪个点相连 int w;//权重是多少 }; vector<node>vi[500];//存i点与哪些点相连 queue<int>fir; int ci[500]; int ui[500]; bool inq[500];//标记在不在队列里 int in[500];//记录入度 int main(){ int n,m; cin>>n>>m; while(!fir.empty()) fir.pop(); memset(inq,0,sizeof(inq)); memset(in,0,sizeof(in)); for(int i=1;i<=n;i++)//输入ci和ui { cin>>ci[i]>>ui[i]; if(ci[i]!=0)//如果是输入层 { fir.push(i);//放到队列里 inq[i]=1;//标记在队列中 } } for(int i=1;i<=m;i++)//输入结点与结点相连的信息 { int u,v,w; cin>>u>>v>>w; node no; no.to=v; no.w=w; vi[u].push_back(no);//与u相连的结点又多了一个 in[v]++; //v结点入度+1 } //fir队列里存放当前这一层,一层层遍历 //相当于来个bfs while(!fir.empty()) { int p = fir.front(); fir.pop(); inq[p]=0; //标记不在队列里 for(int i=0;i<vi[p].size();i++)//拿p.x这个点更新它的下层 { node no = vi[p].at(i); int to = no.to;//to表示与p.x相连的其中一个点 in[to]--;//to的入度-1 if(in[to]==0)//如果这个点没有入度了,说明已经计算完了ci*wi,要减去ui { ci[to]-=ui[to]; } if(ci[p]>0)//如果p.x这个点被激活了,那么就可以根据题目要求更新to的ci了 { ci[to] += no.w*ci[p]; //先更新 if(inq[to]==0)//如果不在队列里,再把它放到队列里去,并更新lay { inq[to]=1;//标记在队列里 fir.push(to); } } } } //找没有出度的点,输出它的值即可 bool f = 0;//是不是没有输出 for(int i=1;i<=n;i++) { if(vi[i].size()==0)//找到输出层 { if(ci[i]>0) { f=1;//有输出 cout<<i<<" "<<ci[i]<<endl; } } } if(!f){ cout<<"NULL"<<endl; } return 0; }