拓扑排序
拓扑排序的构建
到目前为止,我只会从度的方面来构造拓扑,而用dfs或者sort来构建我还没学会
拓扑排序是存在在DAG中,而在DAG中因为是无环的,所以必然存在一个点v1他是入度为0的,那么这个入度为0的点就是当前的出发点。
然后遍历v1的周边的点v2,v3,然后他们的入度个数减去1,这时候会出现两个入度为0的点,然后压入队列(栈),重复操作。
但是拓扑排序有几个特殊情况:
- 如果说这个是个有环的图,那么就在上述操作过程中,一定会出现没有遍历完所有的点就结束操作的情况,那么这个时候我们就可以根据遍历的点和它总共的点的个数来判断是否有环。(这个包括了自己指向自己的情况)
- 如果说题目中说每个点之间都有关系,那么注意在一次中只会出现一个入度为0的点,否则就不可能两两之间的都有明确的关系。
题目
洛谷P1347 排序
传送门
题解
这道题因为她要求求在第几个关系前确定关系,所以每次我们输入一个关系那么我们就要检验一遍拓扑。
#include<iostream>
#include<string>
#include<queue>
#include<vector>
#include<cstdio>
using namespace std;
int g[26][26];//字母间的大小关系
int r[26];//字母的入度的个数
int vis[26];//是否有输入这个字母
queue<int> q;
int order[26];//存最后拓扑序列
int n, m;
int sum = 0, sum1 = 0;
int topo()
{
int ru[26], v = 0, flag = 0;//flag检验环 ,ru也是暂时的入度个数,因为要多次拓扑,为了不影响后面操作,v存当时入度为0 的点的个数
sum = 0;
for(int i = 0; i < n; i++)
{
ru[i] = r[i];
if(!ru[i] && vis[i])//vis很重要,有些字母没输入当然不在图中
{
if(v) flag = 1;
else v = 1;
q.push(i);
order[sum++] = i;
}
}
if(q.empty()) return 1;//如果没有度为0 的点,说明是有环
while(!q.empty())
{
int u = q.front();
q.pop();
v = 0;
for(int i = 0; i < 26; i++)
{
if(g[u][i])
{
ru[i]--;
if(!ru[i])
{//cout <<u << ' ' << i << endl;
if(v) flag = 1;
else v = 1;
q.push(i);
order[sum++] = i;
}
}
}
}//cout << sum1 << sum << endl;
if(sum1 != sum)
return 1;
else if(flag)
return 2;
else
return 0;
}
int main()
{
string str;
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
cin >> str;
if(g[str[0]-'A'][str[2]-'A'] == 0)//题目会出现重复的边,为了不让一个点的入度不多加
{
g[str[0]-'A'][str[2]-'A'] = 1;
r[str[2]-'A']++;
}
if(!vis[str[0]-'A'])
{
sum1++;
vis[str[0]-'A'] = 1;
};
if(!vis[str[2]-'A'])
{
sum1++;
vis[str[2]-'A'] = 1;
};
int flag = topo();
if(flag == 1)
{
printf("Inconsistency found after %d relations.", i) ;
return 0;
}
else if(flag == 0 && sum == n)//已经有了n个不同的数
{
printf("Sorted sequence determined after %d relations: ", i);
for(int i = 0; i < sum; i++)
printf("%c", order[i]+'A');
cout << ".";
return 0;
}
}
cout << "Sorted sequence cannot be determined." << endl;
return 0;
}