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
浙公网安备 33010602011771号