洛谷P1038 神经网络(bfs,模拟,拓扑)

题目背景

人工神经网络(Artificial Neural NetworkArtificialNeuralNetwork)是一种新兴的具有自我学习能力的计算系统,在模式识别、函数逼近及贷款风险评估等诸多领域有广泛的应用。对神经网络的研究一直是当今的热门方向,兰兰同学在自学了一本神经网络的入门书籍后,提出了一个简化模型,他希望你能帮助他用程序检验这个神经网络模型的实用性。

题目描述

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

神经元〔编号为11)

图中,X_1-X_3X1X3是信息输入渠道,Y_1-Y_2Y1Y2是信息输出渠道,C_1C1表示神经元目前的状态,U_iUi是阈值,可视为神经元的一个内在参数。

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

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

C_i=\sum_{j,i \in E} W_{ji}C_{j}-U{i}Ci=j,iEWjiCjUi

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

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

输入输出格式

输入格式:

 

输入文件第一行是两个整数n(1 \le n \le 100)n(1n100)和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”。

 

输入输出样例

输入样例#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

题目类型:

模拟,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;
}

 

 

 

posted on 2019-04-26 22:54  蔡军帅  阅读(215)  评论(0编辑  收藏  举报