一、 【常用】拓扑排序模板(BFS 做法)
#include<bits/stdc++.h>
using namespace std;
const int N=105;
vector<int> G[N];
int in[N],ans[N],n,m,tmp;
int pd; //判断有无环
//BFS拓扑排序
void topu()
{
queue<int> q;
for(int i=1;i<=n;++i)
if(in[i]==0) q.push(i);
int t;tmp=0;
while(!q.empty())
{
t=q.front();q.pop();pd++;
ans[tmp++]=t;
for(int i=0;i<G[t].size();++i)
{
in[G[t][i]]--;
if(in[G[t][i]]==0) q.push(G[t][i]);
}
}
if(pd<n) { //若出队数少于入队数,说明存在入度始终不为 0的节点
cout<<"有环"<<endl;
return;
}
for(int i=0;i<n;++i)
cout<<ans[i]<<' ';
cout<<endl;
}
int main()
{
while(~scanf("%d%d",&n,&m),n>0&&m>0)
{
memset(in,0,sizeof(in));
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;++i) G[i].clear();
//建图
int a,b;
for(int i=0;i<m;++i)
{
cin>>a>>b;
G[a].push_back(b);
in[b]++;
}
topu();
}
return 0;
}
二、 拓扑排序模板(DFS 做法)
#include<bits/stdc++.h>
using namespace std;
const int N=105;
vector<int> G[N],book(N); //book标记一定要初始化
int ans[N];
int n,m,tmp;
bool dfs(int u)
{
book[u]=-1;
for(int i=0;i<G[u].size();++i)
{
if(book[G[u][i]]==-1) {return false;} //如果该节点中有节点也在访问中 说明是有向环
else if(book[G[u][i]]==0) {dfs(G[u][i]);} //如果该节点指向的节点没被访问过 则对其进行递归访问
}
book[u]=1; ans[--tmp]=u;
return true;
}
//DFS拓扑排序
bool topu()
{
tmp=n;
for(int i=1;i<=n;++i)
if(book[i]==0) if(!dfs(i)) {return false;}
return true;
}
int main()
{
while(~scanf("%d%d",&n,&m),n>0&&m>0)
{
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;++i) G[i].clear();
book.clear();
//建图
int a,b;
for(int i=0;i<m;++i)
{
cin>>a>>b;
G[a].push_back(b);
}
if(!topu()) cout<<"有环"<<endl;
else for(int i=0;i<n;++i) //else还可以直接和 for连接
cout<<ans[i]<<' ';
cout<<endl;
}
return 0;
}
关于拓扑排序的小提示
以上面的拓扑图为例,要是用 B F S BFS BFS 拓扑排序,那么结果为 123546 123546 123546,但要是用 D F S DFS DFS 拓扑排序 ,结果为 125634 125634 125634,两者结果不一样
一般情况下,我们想的和 B F S BFS BFS 拓扑排序的结果是一致的,即 4 4 4 和 6 6 6 都在 3 3 3 和 5 5 5 后面,但从 D F S DFS DFS 拓扑排序的结果来看显然不是,它的 3 3 3 在 6 6 6 的前面,我们可以仔细从拓扑排序的定义思考,定义只是规定了箭头所指的元素一定要在箭头后的元素的后面,而 3 3 3 和 6 6 6 没有直接关系,因此是惯性思维导致我们的猜想存在漏洞
补充:一般提到 D A G DAG DAG(有向无环图)就要想到拓扑排序
给任务排序 Ordering Tasks
#include<bits/stdc++.h>
using namespace std;
const int N=105;
vector<int> G[N];
pair<int,int> mmp[N*N]; //pair占两倍空间
int in[N],ans[N],n,m,tmp;
//BFS拓扑排序
void topu()
{
queue<int> q;
for(int i=1;i<=n;++i)
if(in[i]==0) q.push(i);
int t;tmp=0;
while(!q.empty())
{
t=q.front();q.pop();
ans[tmp++]=t;
for(int i=0;i<G[t].size();++i)
{
in[G[t][i]]--;
if(in[G[t][i]]==0) q.push(G[t][i]);
}
}
for(int i=0;i<n;++i)
cout<<ans[i]<<' ';
cout<<endl;
}
int main()
{
while(~scanf("%d%d",&n,&m),n>0) //这里有个坑,不可以加上 &&m>0
{
memset(in,0,sizeof(in));
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;++i) G[i].clear();
//并没有保证关系不重复
int a,b;
for(int i=0;i<m;i++)
{
cin>>a>>b;
mmp[i]=make_pair(a,b);
}
sort(mmp,mmp+m);
int len=unique(mmp,mmp+m)-mmp;
for(int i=0;i<len;++i)
{
G[mmp[i].first].push_back(mmp[i].second);
in[mmp[i].second]++;
}
topu();
}
return 0;
}
思路
其实就是模板题,只是题目并没有保证输入的数据不重复,因此要加上
p
a
i
r
pair
pair 方法去重
补充:关于使用 p a i r 、 s o r t 、 u n i q u e pair、sort、unique pair、sort、unique 去重可以参考 C++ STL库 容器与方法 的 p a i r pair pair 章
神经网络
#include<bits/stdc++.h>
using namespace std;
const int N=105;
vector<int> G[N];
queue<int> q;
int out[N],C[N],hd[N];
bool book[N];
int n,m,a,b,c,cnt=0,flag=0,flag_sum=0;
struct Edge {
int to,val,nxt;
}e[N*N];
void build(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].val=w;
e[cnt].nxt=hd[u];
hd[u]=cnt;
}
void topu()
{
while(!q.empty())
{
int tmp=q.front();
q.pop();
for(int i=hd[tmp];i;i=e[i].nxt)
{
C[e[i].to]+=C[tmp]*e[i].val;
if(C[e[i].to]>0&&book[e[i].to]==0) { //只有状态大于 0的神经元才能继续传递信息
book[e[i].to]=1;
q.push(e[i].to);
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i)
{
cin>>a>>b;
//这里不可以直接减,初始层也有可能有阈值,但不能减去(题目要求)
if(a>0)
{
C[i]=a;
book[i]=1;
q.push(i);
}
else C[i]=a-b;
}
for(int i=0;i<m;++i)
{
cin>>a>>b>>c;
build(a,b,c);
out[a]++; //因为每层节点指向的一定是下一层的节点,所以不用记录入度,出度是用来判断输出层的
}
topu();
for(int i=1;i<=n;++i)
{
if(out[i]==0)
{
flag_sum++;
if(C[i]>0) cout<<i<<' '<<C[i]<<endl; //只输出状态大于 0的输出层的神经元
else if(C[i]==0) flag++;
}
}
if(flag_sum==flag) cout<<"NULL"<<endl; //若输出层的神经元最后状态均为 0,则输出 NULL
return 0;
}
思路
题目意思就是从输入层到输出层的神经元逐个传递信息,最后输出状态不为 0 的输出层神经元
因为是
D
A
G
DAG
DAG 所以想到用拓扑排序,但并不是套用模板(因为根本用不到入度),只是运用了思想
其实题挺简单的,就是坑有点多,比如输入层的神经元不能减去阈值 (然而题目根本没说啊)
困扰我半小时的坑点:不能在每次
w
h
i
l
e
while
while 循环开始时才 book[tmp]=1
,要在最开始记录输出层时 book[i]=1
和 每次判断结束后 book[e[i].to]=1
,是因为判断条件有 book[e[i].to]==0
,如果用前者方法标记,那么在遍历到下一层节点前就并不会被标记到,也就是标记没有用,从而导致 C[e[i].to]+=
重复计算
补充:关于链式前向星可以参考 这篇博客