VAS图(我的理解)就是由一系列计划得关系得出的单项图例如c1->c2表示要进行c2必须要进行c1,描述完所有计划的先后关系后,也就得到了一张图,对这张VAS图进行排序就是拓扑排序,排序得结果就是所有计划进行得先后顺序,因为存在并列的计划,所以排序结果并不一定唯一。
思想就是利用图论得一些知识
先对队列进行初始化——循环遍历所有的点,如果发现点的入度为0的化就入队,由于题目大都会加上一个限制:尽量使得小的点在前,以确保答案唯一,所以采用了优先队列
for(int i = 1;i <= n;i++)
{
if(in[i] == 0)
{
myq.push(i);
}
}
接着就开始出队,并且,进行出对后的更新优化,出队一个点,把这个点所有的后继点的入度-1,如果发现入度因此为0的话,继续入队,直至队列为空
while(myq.size())
{
int a = myq.top();
myq.pop();
ret[len++] = a;
for(int i = 0;i < nex[a].size();i++)
{
if(--in[nex[a][i]] == 0)myq.push(nex[a][i]);
}
}
上述的拓扑排序为正向的拓扑适用于HDU1285
而面对较为复杂的关系条件约束的时候,可能就需要逆向的思维了——逆向拓扑排序
说白了,正向的拓扑利用的是入度,一个一个的进出,而逆向的是利用出度,先确定尾巴,在确定头,例如HDU4857,它多了一个这样的约束条件:负责人现在可以安排大家排队的顺序,由于收了好处,所以他要让1号尽量靠前,如果此时还有多种情况,就再让2号尽量靠前,如果还有多种情况,就让3号尽量靠前,以此类推。 ——意思大概是1的第二优先级最高,在符合第一优先级的基础上要先确保1在前面,才能去管2,3,……,所以,你再看看正向就不好说了,这里引用一下不吸血的Vampire大佬的博客
满足要求的拓扑序应该是:6 4 1 3 9 2 5 7 8 0。
如果利用正向的拓扑排序,由于对小数的照顾我们会先出3,就会错误了,一i那位我们要1尽可能的在前面,而如果1深深的藏在里面,我们正向去找就会非常的麻烦
引用:
让我们换个角度想一想,节点 3 和 6,应该是 6 先出队列,因为节点 1 在 6 的后面。这和节点 3 和 6 的编号大小没有任何关系。但是,再看另外两条路径的尾部,节点 2 和 8,可以肯定地说,2 一定先出队列,因为它们后面都没有别的节点了,这个时候完全以这两个节点本身的编号大小决定顺序。归纳起来就是说,对于若干条平行的路径,小的头部不一定排在前面,但是大的尾部一定排在后面。于是,就有了算法 3。
1. 把所有出度为 0 的节点放进优先队列 PQ
2. WHILE: PQ 不是空队列
3. 从 PQ 中取出编号最大的元素 a,把 a 添加到答案的头部。
4. FOR:所有指向 a 的边 b → a
5. 把 b 的出度减 1。如果 b 的出度变为 0,则把 b 放进优先队列PQ。
理解了上面的思路代码就可以独立的完成了,有点小激动,懂了之后,自己实现去,一次AC了
#include <iostream>
#include <vector>
#include <queue>
#include <cstdio>
#include <string.h>
using namespace std;
const int maxn = 1e5 + 1e4;
const int maxp = 3e4 + 3e3;
int out[maxp];
vector<int> pre[maxp];
int ret[maxp];
priority_queue<int> q;
void init(int n)
{
memset(out,0,sizeof(out));
for(int i = 1;i <= n;i++)
{
pre[i].clear();
}
while(q.size())q.pop();
}
void toposort(int n)
{
for(int i = 1;i <= n;i++)
{
if(out[i] == 0)
{
q.push(i);
}
}
int len = n;
while(q.size())
{
int a = q.top();
q.pop();
ret[--len] = a;
for(int i = 0;i < pre[a].size();i++)
{
if(--out[pre[a][i]] == 0)q.push(pre[a][i]);
}
}
}
int main()
{
int t,n,m,a,b;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init(n);
for(int i = 0;i < m;i++)
{
scanf("%d%d",&a,&b);
out[a]++;
pre[b].push_back(a);
}
toposort(n);
for(int i = 0;i < n;i++)
{
if(!i)printf("%d",ret[i]);
else printf(" %d",ret[i]);
}
printf("\n");
}
return 0;
}