拓扑排序,其本质是输出一个全序关系,对于按要求输出给定关系的题目,一般就是按照题目要求实现这个全序关系,这种题时常会先给一个偏序关系,然后给出剩下的元素如何建立关系(字典序之类的)。
如果忘了那几个词是啥意思...
偏序关系:满足自反,反对称,传递性的关系
全序关系:一个偏序关系R,且对任意x,y有xRy或yRx
哈斯图:对一个偏序关系画的图,每个点为关系中的元素,其中,对两点x,y,若有xRy则y画在x上方,若有xRy且不存在s满足xRs,sRy则在x,y中连线。一般情况下把题目里面给的关系直接画出来再分下层就是哈斯图了。和正常的哈斯图不同,为了方便看我们一般会在上面标上箭头。
拿下面这个图说事,按之前的说法,6-2和6-1都在这个偏序关系里面(传递性)但是因为6和1里面连着2就不在他俩连线了,同样对于拓扑排序的题,题目里面说了6-2和2-1以后由传递性就有6-1了,不要拘泥于概念
画上箭头。拓扑排序的效果在于把偏序关系线性输出,在这个里面5,6,7之间没有确定的顺序,还有就是可以按照8-5-6-2这样的顺序,在7之前输出2,没有说同层的必须连着输出。对于这种状况按什么顺序输出是一个考点,经常会把这个顺序根点的标号扯上关系,加上拓扑排序里面用到一个队列,所以时常会在此处把队列操作成优先队列
然后就是拓扑排序可以用于判断有向环,如果在排序中排出来的点比图上的总点数少就说明有有向环。如果一个关系里面出现了环,自然就不满足反对称性了(ex:xRy,yRz,zRx则由传递性有xRz与zRx一起不满足反对称性),然后它就不是一个偏序关系了,所以如果判出来了环的情况论上就不会有下一步操作了。无向环用并查集判断
另外还有的题就是要你确定一个偏序关系,比如统计同样的偏序(可以理解为哈斯图上面同一层的元素)每个有多少
因为拓扑排序这个东西本身比较直观符合大众认知,直接给个裸的拓扑排序显得太亲民,所以如果看见他出现,一般都会是绕了几个弯,变成一道智商题,想直接写一个拓扑排序然后就过了不大现实,搞清楚拓扑排序的思想比较重要,方便在关键时刻能够胡搞的出来。
常用操作:
反向建图:有时会出现=需要把题目所给的偏序反过来的情况,比如下面的2647
把队列换成优先队列
hdu1285
比较纯的拓扑排序,因为要按照字典序输出拓扑序相同的元素,以及时间多,所以可以不使用队列而是暴力遍历
#include
using namespace std;
const int maxn=500;
int main()
{
int v,e;
while(cin>>v>>e)
{
vectorvec;
vector mp[maxn];
int deg[maxn],s,t;
memset(deg,0,sizeof(deg));
for(int i=1;i<=e;i++)
{
cin>>s>>t;
deg[t]++;
mp[s].push_back(t);
}
for(int i=1;i<=v;i++)
{
for(int j=1;j<=v;j++)
{
if(deg[j]==0)
{
for(int k=0;k
拓扑排序判断有向环的题目,对于判环的题,判无向环一般用并查集,判有向环一般用拓扑排序
#include
using namespace std;
int main()
{
vector adjl[105];
int v,e,s,t,in[105];
bool vis[105];
while(cin>>v>>e,v!=0)
{
memset(adjl,0,sizeof(adjl));
memset(vis,0,sizeof(vis));
memset(in,0,sizeof(in));
for(int i=0;i>s>>t;
adjl[s].push_back(t);
in[t]++;
}
int coun=v;
queue q;
for(int i=0;i
hdu2647
使用拓扑排序判环并且判断点在拓扑序上的层次,我的思路是维护两个值,一个值记录现在的层次,另一个值维护当前层次下,队列中还有多少个元素。另外还要反向建图
#include
using namespace std;
int main()
{
vector adjl[10005];
int v,e,s,t,in[10005],deg[10005];
bool vis[10005];
while(cin>>v>>e)
{
memset(adjl,0,sizeof(adjl));
memset(vis,0,sizeof(vis));
memset(in,0,sizeof(in));
for(int i=0;i>s>>t;
adjl[t].push_back(s);
in[s]++;
}
int coun=v;
queue q;
int levl=0,cur=0,tot=0;
for(int i=1;i<=v;i++)
{
if(in[i]==0)
{
q.push(i);
vis[i]=true;
levl++;
}
}
// for(int i=0;i
hdu1811
#include
using namespace std;
const int maxn=10005;
int fa[maxn];
int findfa(int vt)
{
if(vt!=fa[vt]) fa[vt]=findfa(fa[vt]);
return fa[vt];
}
struct edge
{
int from,to;
char op;
}ed[maxn*2];
int main()
{
int v,e,s,t,in[maxn];
vector adjl[maxn];
while(~scanf("%d%d",&v,&e))
{
for(int i=0;i')
{
adjl[x].push_back(y);
in[y]++;
}
else{adjl[y].push_back(x);in[x]++;}
}
queueq;
for(int i=0;i1)flag=true;
int vt=q.front();
q.pop();
sum--;//cout<<" !"<0)cout<<"CONFLICT"<
hdu 5695
这个题把拓扑排序用的队列换成优先队列以后再处理一下就行了
#include
using namespace std;
vector adjl[100010];
long long c,su;
int v,e,s,t,in[100010];int ans[100010],countt;
void top_sort()
{priority_queue q;
for(int i=v;i>=1;i--)
if(in[i]==0)
q.push(i);
while(!q.empty())
{
int vt=q.top();
q.pop();
ans[countt++]=vt;
//c=min((long long)vt,c);
//su+=cans[i]?ans[i]:c;
su+=c;
}
printf("%I64d\n",su);
//for(int i=0;i
反向建图,大根堆做队列,反向输出
#include
using namespace std;
const int maxn=30005;
int main()
{
int v,e,in[maxn];
bool vis[maxn];
int tt;
vector adjl[maxn];scanf("%d",&tt);
while(tt--)
{scanf("%d %d",&v,&e);
for(int i=1;i<=v;i++)
{
adjl[i].clear();
in[i]=0;
vis[i]=false;
}
int fr,to;
for(int i=1;i<=e;i++)
{
scanf("%d %d",&fr,&to);
adjl[to].push_back(fr);
in[fr]++;
}
int coun=1;
//top_sort;
priority_queueq;
vectorvec;
for(int i=1;i<=v;i++)if(in[i]==0){q.push(i);}
while(!q.empty())
{
int vt=q.top();
vec.push_back(vt);
// cout<0;i--)printf("%d ",vec[i]);
printf("%d\n",vec[0]);
}
return 0;
}