提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
拓扑排序
维基百科上的解释如下:
当且仅当图中没有定向环时(即有向无环图),才有可能进行拓扑排序。
任何有向无环图至少有一个拓扑排序。已知有算法可以在线性时间内,构建任何有向无环图的拓扑排序。
在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,才能称为该图的一个拓扑排序(英语:Topological sorting):
序列中包含每个顶点,且每个顶点只出现一次;
若A在序列中排在B的前面,则在图中不存在从B到A的路径。
一个图能进行拓扑排序的充要条件是它是一个有向无环图(DAG)
步骤:
基于BFS:
1.找到所有入度为0的点放进队列作为起点谁先谁后没关系
2。弹出队首,其所有邻居点入度减一在次把入度为0的点放入队列
3.直到队列为空
基于DFS:
如果想按正确顺序打印出拓扑序则要用栈
一、 确定字典序最小的拓扑排序
(用邻接矩阵存图)BFS+优先队列(hdu1285)
#include<bits/stdc++.h>//图论拓扑排序+优先队列hdu1285
using namespace std;
//hdu1285 DFS拓扑排序加优先队列
bool mp[517][517];
int in[517];//记录入度
priority_queue<int,vector<int>,greater<int> > q;//每次弹出最小值的优先队列 ,得到队伍编号最小的排名
void tuopu(int n)
{
for(int i=1;i<=n;i++)
{
if(in[i]==0)
q.push(i);//把入度为0的压入队列
}
int c=1;
while(!q.empty())
{
int v=q.top();
q.pop();
if(c!=n)//没到尾
{
cout<<v<<" ";
c++;
}
else
cout<<v<<endl;
for(int i=1;i<=n;i++)
{
if(!mp[v][i])
continue;
in[i]--;//入度减一
if(!in[i])
q.push(i);//把入度为0的压入队列
}
}
}
int main()
{
int n,m,i,j;
while(cin>>n>>m)
{
int k=0;
memset(mp,0,sizeof(mp));
memset(in,0,sizeof(in));
while(m--)
{
cin>>i>>j;
if(mp[i][j])
continue;
mp[i][j]=1;
in[j]++;
}
tuopu(n);
}
}
二, 按字典序从小到大输出所有拓扑排序
poj1270
#include<iostream>//poj1270拓扑排序,邻接矩阵dfs实现,重要
#include<algorithm>
#include<cstring>
using namespace std;
int mp[30][30],in[30],tp[30],n;
char s[30];
int getit(int ch)
{
for(int i=1;i<n;i++)
if(s[i]==ch) return i;
}
bool init()//输入
{
char ch;
n=0;
while((ch=getchar())!='\n')
{
if(ch==EOF) return false;
if(ch!=' ') s[++n]=ch;
}
s[++n]='\0';
sort(s+1,s+n);
memset(in,0,sizeof(in));
memset(mp,0,sizeof(mp));
while(true)//输入约束对
{
while((ch=getchar())==' ');
if(ch==EOF) return false;
if(ch=='\n') break;
int u=getit(ch);
while((ch=getchar())==' ');
int v=getit(ch);
mp[u][v]=1;//邻接矩阵储存
in[v]++;//入度增加
}
return true;
}
void dfs(int now)
{
if(now==n) //到结尾
{
for(int i=1;i<n;i++)
{
cout<<s[tp[i]];//输出拓扑序
}
cout<<endl;
return;
}
for(int i=1;i<n;i++)
{
if(in[i]==0)
{
in[i]=-1;
tp[now]=1;//入度为0的在第一位
for(int j=1;j<n;j++)
if(mp[i][j]) in[j]--;
//把该结点的子节点入度递减
dfs(now+1);
in[i]=0;//恢复现场
for(int j=1;j<n;j++)
if(mp[i][j]) in[j]++;
}
}
}
void work()
{
while(init())
{
dfs(1);
cout<<endl;
}
}
int main()
{
work();
return 0;
}
三,拓扑序找环,判断是否有环
hdu3342
如果队列已空但是还有点未进入队列则这些点入度都不是0,说明图不是DAG
#include<bits/stdc++.h>//hdu3342拓扑序找环
using namespace std;
int n;
bool m[105][105];//邻接矩阵储存
int master[105];//指向的徒弟的师傅数量(入度)
int main()
{
int n,M,u,v;
while(scanf("%d%d",&n,&M)!=EOF and n)
{
memset(master,0,sizeof(master));
memset(m,false,sizeof(m));
while(M--)
{
scanf("%d%d",&u,&v);
if(!m[u][v])
{
m[u][v]=true;
++master[v];
}
}
bool flag;
int num=0;//人数
for(;;)
{
flag=true;
for(int u=0;u<n;u++)//找出master数量为0的人
{
if(master[u]==0){
num++;
master[u]=-1;
flag=false;
for(int v=0;v<n;v++)
{
if(m[u][v]) master[v]--;//入度递减
}
}
}
if(flag) break;
}
if(num==n) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
四,判断有无环+反向建图
hdu2647
#include<bits/stdc++.h>//hdu3342拓扑序找环
using namespace std;
int n;
bool m[105][105];//邻接矩阵储存
int master[105];//指向的徒弟的师傅数量(入度)
int main()
{
int n,M,u,v;
while(scanf("%d%d",&n,&M)!=EOF and n)
{
memset(master,0,sizeof(master));
memset(m,false,sizeof(m));
while(M--)
{
scanf("%d%d",&u,&v);
if(!m[u][v])
{
m[u][v]=true;
++master[v];
}
}
bool flag;
int num=0;//人数
for(;;)
{
flag=true;
for(int u=0;u<n;u++)//找出master数量为0的人
{
if(master[u]==0){
num++;
master[u]=-1;
flag=false;
for(int v=0;v<n;v++)
{
if(m[u][v]) master[v]--;//入度递减
}
}
}
if(flag) break;
}
if(num==n) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}