1. 程式人生 > >【p1038】洛谷P1038神經網絡題解

【p1038】洛谷P1038神經網絡題解

style sin bool lin \n 基本上 一個點 CA 拓撲排序

這個題不得不說是一道大坑題,為什麽這麽說呢,這題目不僅難懂,還非常適合那種被生物奧賽刷下來而來到信息奧賽的學生。

因此我們先分析一下題目的坑點。

1:

題目的圖分為輸入層,輸出層,以及中間層。

我們怎麽判斷呢???可以判斷每個點的入度及出度。如果一個點的入度為零則它是輸入層,出度為零則是輸出層。其余情況便是中間層。

因為根據原題所描述的

技術分享圖片公式中的 Wji? (可能為負值)表示連接 j 號神經元和 i 號神經元的邊的權值。

C_i 大於 0 時,該神經元處於興奮狀態,否則就處於平靜狀態。當神經元處於興奮狀態時,下一秒它會向其他神經元傳送信號,信號的強度為 C_i?

我們可以得出只有一個點是終點時,才會滿足上述公式,或者說是(非輸入層),因此我們可以想到如果想讓一個中間層或輸出層的狀態確定,那麽與他相連的邊的Cj

值都要乘上,如果有一條不乘上,就不滿足。

很容易想到現在所有的Cj的值必須是已經算出來的了。

因此我們可以想到拓撲排序,因為拓撲排序的條件就是這個點與他相連的邊的起點都要先完成他們的任務。

2:

根據上文所知,C值一開始是不確定的,所以我們可以新建一個now數組,來表示現在這個點的狀態值,當他相連邊的起點都加到now的值上時,即入度為0,那此時的now值就是他的狀態值。

但是這裏有一個大坑點:只有這條邊的起點的C值>0時才可以加上。

並且你不能在剛取出這個起點的時候就判斷(別問我怎麽知道的)。

因為如果你判斷了,那終點的入度就減不了了。

用拓撲排序的套路,把這個點入隊。再反復進行上面的幾步。

分析完這個題的坑點,基本上這個題就解決了。

代碼:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
queue<int>q;
int n,m,in_degree[1000],out_degree[1000];
bool flag;
int tot,lin[100010];
int c[10010],u[1010],now[10000];//now是現在的狀態值,並不是最終的。 
struct cym {
    int from,to,next,len;
} e[100010];
void add(int
x,int y,int v) { e[++tot].from=x; e[tot].to=y; e[tot].len=v; e[tot].next=lin[x]; lin[x]=tot; in_degree[y]++;//判斷是否是輸出層和輸入層及拓撲排序 out_degree[x]++; } int main() { scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) scanf("%d%d",&c[i],&u[i]); for(int i=1; i<=m; i++) { int x,y,v; scanf("%d%d%d",&x,&y,&v); add(x,y,v); } for(int i=1; i<=n; i++) if(!in_degree[i])//如果這個點是輸入層他的狀態是最先確定的,因此根據FIFO原則,用隊列優化,也是拓撲排序的方法。 q.push(i); while(!q.empty()) { int cur=q.front(); q.pop(); //if(c[cur]<=0)加上這兩句是不行的,因為要減去下文的入度 //continue; for(int i=lin[cur]; i; i=e[i].next) { in_degree[e[i].to]--; if(c[e[i].from]>0)//判斷該點是否可以向下傳遞 now[e[i].to]+=c[e[i].from]*e[i].len; if(!in_degree[e[i].to])//當入度為零時說明所有先決條件都已滿足,滿足拓撲排序,可以入隊 { now[e[i].to]-=u[e[i].to];//別忘了減去閾值 c[e[i].to]=now[e[i].to]; q.push(e[i].to); } } } for(int i=1; i<=n; i++) if(out_degree[i]==0&&c[i]>0)//判斷是否為輸出層且最後狀態>0 { flag=1; printf("%d %d\n",i,c[i]); } if(!flag) printf("NULL"); }

【p1038】洛谷P1038神經網絡題解