2021-08-01

 19:56:56

题目:

https://www.luogu.com.cn/problem/P1038

描述:

题目背景

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

 

题目描述

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

 

 

神经元〔编号为 1)

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

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

 

 

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

Ci=∑​(Wji*Cj​−Ui)

(j,i)∈E

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

 

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

 

输入格式

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

 

输出格式

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

若输出层的神经元最后状态均为 0,则输出 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

 

 

题目分析:

从题干来看,可以用拓扑排序做,但是题目数据分为输入层,中间层,输出层。

我们看样例

 

 可以看出,输入层的数据(样例中为第2~3行),第一位都是非0数,而中间层的数据,第一位都是0.所以我们可以利用这个特点处理数据

由于题目n的范围是0<=n<=100,所以const N=105。

构造结构体 Node,储存每个点的state(状态)和threshold(阈值)。

所以可以写出以下代码

 

#include<iostream>
using namespace std;
const int N=105;
int n,p;
struct Node{
    int state,threshold;
}node[N];
int main()
{
    cin>>n>>p;
    for(int i=1;i<=n;i++){
        int x,y;
        cin>>x>>y;
        node[i]={x,y};
    }
    return 0;
}

 

处理完了前n个数据之后,我们要设计结构使得满足神经元的设计,即

 所以我们可以在接下来的p个输入数据里,利用vector储存下级的点,(所谓下级的点就是与这个点的输出层相连的点)

于是我们使用vector来储存,

例如样例里面的最后p个数:

1 3 1  ==>3是1的下级点,且权重为1

1 4 1

1 5 1

2 3 1

2 4 1

2 5 1

构造如图

 

 

 再剩下的p个数据中,用vector数组实现这个结构,并用g[N][N]储存所有边的权重,用rudu[N]储存所有点的入度

所以补充至以下代码:

 

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int N=105;
int n,p;
int g[N][N];//g[i][j]:储存从j的上一个信息输入端i指向j的权重
int rudu[N];//记录每一个点的入度,入度为0时即可加入队列
struct Node{
    int state,threshold;
}node[N];
vector<int>vec[N];//vec[i]表示:第i个点联结哪些点

int main()
{
    cin>>n>>p;
    for(int i=1;i<=n;i++){
        int x,y;
        cin>>x>>y;
        node[i]={x,y};
    }
    for(int i=1;i<=p;i++){
        int x,y,w;
        cin>>x>>y>>w;
        g[x][y]=w;
        vec[x].push_back(y);//x的下一级有y
        rudu[y]++;
    }

    return 0;
}

 

 

 

  

处理完数据之后,我们需要将每个中间层的点的状态更新一下,这样才能求出最后的输出层的值

所以我们得写一个函数fac()

这个函数:首先得将所有入度为0的点加入队列q里面去,如果while循环到队列q的长度=0的时候,则所有点都遍历过一次

代码如下:

(这里新增加了一些变量如cnt,这些都是在函数之外定义的)

(我们在这里是使用cnt作为解的个数的,因为题目在输出格式中讲到:按编号输出输出层中非0的数)

对于各个点的更新:

 

 

 各个点在更新的时候只需要减去一个Ui(阈值)就行了,所以用st[N]数组判断是否减去了一个阈值

 

 1 void fac(){
 2     memset(st,false,sizeof st);
 3     //用队列维护
 4     queue<int>q;
 5     //每次将rudu[i]为0的点加入队列
 6     for(int i=1;i<=n;i++){
 7         if(rudu[i]==0)q.push(i);
 8     }
 9     while(q.size()){
10         int t=q.front();
11         q.pop();
12         //如果点t没有下一级了,就说明点t是最后一级,可以作为输出层输出
13         if(vec[t].size()==0){
14             a[cnt].first=t,a[cnt].second=node[t].state;
15             cnt++;
16         }
17         
18         for(int i=0;i<vec[t].size();i++){
19             int tmp=vec[t][i];
20             node[tmp].state+=g[t][tmp]*node[t].state;
21             if(!st[tmp]){
22                 node[tmp].state-=node[tmp].threshold;
23                 st[tmp]=true;//已经减去阈值
24             }
25             rudu[tmp]--;
26             //如果下一级点是非正数,必须得将其更新为0
27             if(rudu[tmp]==0){
28                 if(node[tmp].state<0)node[tmp].state=0;
29                 q.push(tmp);
30             }
31         }
32     }
33 }

 

 

 

 

  

 最后,由于可能有输出层全为0的情况,所以我们得使用一个变量r来记录输出层中结果为0的数目。

如果全部为0,则输出NULL

代码如下:

 

1 int r=0;
2     for(int i=0;i<cnt;i++){
3         if(a[i].second>0)cout<<a[i].first<<" "<<a[i].second<<endl;
4         else if(a[i].second==0){
5             r++;
6         }
7     }
8     if(r==cnt)cout<<"NULL"<<endl;

 

 

 

  

所以结合以上部分代码,组成下面的完全版代码

 

 

完整代码:

 

 1 #include<iostream>
 2 #include<cstring>
 3 #include<vector>
 4 #include<queue>
 5 using namespace std;
 6 typedef pair<int,int>PII;
 7 const int N=105,M=N*N/2;
 8 int n,p,cnt=0;
 9 int g[N][N];//g[i][j]:储存从j的上一个信息输入端i指向j的权重
10 int rudu[N];//记录每一个点的入度,入度为0时即可加入队列
11 struct Node{
12     int state,threshold;
13 }node[N];
14 bool st[N];//st[i]表示点i有没有减去过阈值
15 vector<int>vec[N];//vec[i]表示:第i个点联结这哪些点
16 PII a[N];//储存最后的输出层,排序后输出
17 void fac(){
18     memset(st,false,sizeof st);
19     //用队列维护
20     queue<int>q;
21     //每次将rudu[i]为0的点加入队列
22     for(int i=1;i<=n;i++){
23         if(rudu[i]==0)q.push(i);
24     }
25     while(q.size()){
26         int t=q.front();
27         q.pop();
28         //如果点t没有下一级了,就说明点t是最后一级,可以作为输出层输出
29         if(vec[t].size()==0){
30             a[cnt].first=t,a[cnt].second=node[t].state;
31             cnt++;
32         }
33         
34         for(int i=0;i<vec[t].size();i++)
35         {
36             int tmp=vec[t][i];
37             node[tmp].state+=g[t][tmp]*node[t].state;
38             if(!st[tmp]){
39                 node[tmp].state-=node[tmp].threshold;
40                 st[tmp]=true;//已经减去阈值
41             }
42             rudu[tmp]--;
43             //如果下一级点是非正数,就没有必要加入队列了
44             if(rudu[tmp]==0)
45             {
46                 if(node[tmp].state<0)node[tmp].state=0;
47                 q.push(tmp);
48             }
49         }
50     }
51 }
52 
53 int main()
54 {
55     cin>>n>>p;
56     for(int i=1;i<=n;i++){
57         int x,y;
58         cin>>x>>y;
59         node[i]={x,y};
60     }
61     for(int i=1;i<=p;i++){
62         int x,y,w;
63         cin>>x>>y>>w;
64         g[x][y]=w;
65         vec[x].push_back(y);//x的下一级有y
66         rudu[y]++;
67     }
68 
69     fac();
70 
71     int r=0;
72     for(int i=0;i<cnt;i++){
73         if(a[i].second>0)cout<<a[i].first<<" "<<a[i].second<<endl;
74         else if(a[i].second==0){
75             r++;
76         }
77     }
78     if(r==cnt)cout<<"NULL"<<endl;
79     return 0;
80 }

 

 

 

 

2021-08-01

20:19:30

 

posted on 2021-08-01 20:20  Dragon昴  阅读(688)  评论(4)    收藏  举报