题解P1137【旅行计划】(拓扑排序)
hicc0305
2018-04-29 16:50:07
以前都一直不懂拓扑排序是个什么东西,看了一下发现还是蛮简单的
拓扑排序是给一个有向无环图(DAG)上的点进行排序,排成线性序列,使排序后的点满足所有的边u->v,在这个序列中,u都在v的前面。
如果还是没明白的话,来举个栗子,如下图
![](https://cdn.luogu.com.cn/upload/pic/18278.png)
序列v6–>v1—->v4—>v3—>v5—>v2就是一个满足拓扑排序要求的序列,当然v1–>v6—->v4—>v3—>v5—>v2,也是可以的
具体怎么做呢?首先找到入度为0的点。因为入度为0的点肯定排在最前面,任意去掉一个入度为0的点和它的边,再次统计入度为0的点并重复这个过程,像这样:
![](https://cdn.luogu.com.cn/upload/pic/18280.png)
我们先去掉了v6,这时入度为0的点只剩下v1,所以再去掉v1
![](https://cdn.luogu.com.cn/upload/pic/18281.png)
这时v3,v4都可以去掉,我们去掉v4
![](https://cdn.luogu.com.cn/upload/pic/18282.png)
然后再是v3
![](https://cdn.luogu.com.cn/upload/pic/18283.png)
再是v5,v2,就得到了v6–>v1—->v4—>v3—>v5—>v2这个序列
最后得到的序列会因程序而异。
------------
------------
## 题解部分
很明显这题可以用拓扑排序,只需要记录一下每个点是第几轮被去掉的就可以了。因为第几轮被去掉就代表了它之前有几个点过来。这里的一轮是指所有入度为0的点同一时间去掉算一轮。具体看代码吧。
```cpp
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,cnt=0;
int head[200100],nxt[200100],to[200100];
int in[100100],ans[100100];
int q[100100],f[100100];
void addedge(int x,int y)
{
cnt++;
nxt[cnt]=head[x];
head[x]=cnt;
to[cnt]=y;
}
void tpsort()
{
int l=0,r=0;
for(int i=1;i<=n;i++)
if(in[i]==0) q[++r]=i,f[i]=1;//先将入度为0的点进队列,并且记录下这些点将在第一轮被去掉。
while(l<=r)
{
int u=q[++l];
for(int i=head[u];i!=-1;i=nxt[i])
{
int v=to[i];
if(f[v]) continue;
in[v]--;//去掉与这个点有关的边,相连的点的入度减1
if(in[v]==0)
{//如果相连的点入度为0了,进队列,进行删点
f[v]=f[u]+1;//可以说是一个小小的DP,这个点必然是在当前点的后一轮删除的
q[++r]=v;
}
}
}
}
int main()
{
memset(in,0,sizeof(in));
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);//加边
in[y]++;//记录入度
}
tpsort();
for(int i=1;i<=n;i++)
cout<<f[i]<<endl;
return 0;
}
```