题目描述
你需要构造一个11到nn的排列,使得它满足mm个条件,每个条件形如(ai,bi)(ai,bi),表示aiai必须在bibi前面。在此基础上,你需要使它的字典序最小。
输入数据
第一行两个正整数nn,mm。接下来mm行每行两个数ai,biai,bi。
输出数据
输出一行nn个整数表示答案。如果不存在这样的排列,输出−1−1。
样例输入
5 4
5 4
5 3
4 2
3 2
样例输出
1 5 3 4 2
数据范围
对于20%的数据,n,mleq10n,mleq10。
对于40%的数据,n,mleq200n,mleq200。
对于60%的数据,n,mleq1000n,mleq1000。
对于100%的数据,n,mleq100000n,mleq100000。
题目分析
我默认大家都已经会priority_queue的用法了,如果你不会的话,我觉得我这里没有讲这个方面的。
这道题是典型的题目啊!求排序方案数,最优解。你记好:看到排列顺序第一个想到的不是单调优化DP,而是拓扑排序。这是一个求状态的问题的神奇算法,取名topsort。
我们把这n个点都看作是一个点集,编号从1到n。所以一个点的情况是:没有约束条件,或者是至少一个约束条件。有点差分系统的风味了。到这里就可以做了,把边建出来(就是把一个点连到另一个点),用一个队列记录一下起始点,greater的队列是最小堆,我们在取出边的时候继续拓展。用网络流的角度来说:从汇点往回跑即可。这样的点一定是在约束条件下的最佳状态。
最后输出-1的时候,当然是这个队列没有办法开到n次了,比如说出现了矛盾,就是环。判断环可以用dj和spfa,当然大法师也可以。
#include<bits/stdc++.h> using namespace std; #define RE register int #define IL inline #define N 200001 priority_queue<int,vector<int>,greater<int> >q; int m,n,cnt,top,head[N],in[N],out[N],ans[N]; struct aa{int v,next;}e[N]; IL void addedge(RE u,RE v){ e[++cnt]=(aa){v,head[u]},head[u]=cnt,in[v]++; }int main(){ freopen("dictionary.in","r",stdin),freopen("dictionary.out","w",stdout); cin>>n>>m; for (RE i=1,u,v;i<=m;++i) cin>>u>>v,addedge(u,v); for (RE i=1;i<=n;++i) if (!in[i]) q.push(i); while(q.size()){ RE u=q.top(); ans[++top]=u,q.pop(); for (RE i=head[u];i;i=e[i].next) if(!(--in[e[i].v])) q.push(e[i].v); }if(top<n) puts("-1"); else{for (RE i=1;i<=n;++i) cout<<ans[i]<<" "; }return 0; }
代码说明
我两次才AC,原因是建边的时候,head[u]=e[i].next,我写反了,邻接表的实现哦。我希望我有时间说一下,因为各种自动机也要用。可惜我没法讲,因为太简单了。大家可能不喜欢我的码风,那个freopen我不推荐写在define,我可是有血的教训的。
上课的时候看小情侣传纸条。