本题坑点
题目没有给数据规模,我去别的oj网站上查了,是 100%的数据满足N,M<=100000,D<=3
解题思路
“看到x要先于y”这类字的时候,第一反应就是拓扑排序(不知道拓扑排序点这里 ),但是有一个问题,如果用拓扑排序,我们只能得到字典序最小的方案, 不能满足题目要求的使得小A能尽量先吃到质量高的菜肴,那怎么解决这个问题呢?我们在做拓扑排序的过程中,会优先考虑路线的起点编号大小,但是我们的目标却是使得每次选取的路线终点的编号最小,那么我们可以通过反向建边,寻找字典序最大的方案,最后反序输出就可以了
注意事项
重边问题
本题存在重边,所以不能在加边的时候就统计入度,去除重边的方法很多,我这里用的是stl里面的set
多组数据
记得每次清空各种数组和变量,防止影响下一组数据
代码实现
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<set> using namespace std; const int mx=101010; int n,m; inline int Read(){ int x=0; char c=getchar(); while(c>'9'||c<'0')c=getchar(); while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar(); return x; } struct Heap{ int size; int h[mx]; void push(int x){ h[++size]=x; int now=size; int fa=now>>1; while(fa>=1){ if(cmp(h[now],h[fa])){ swap(h[fa],h[now]); now=fa; fa=now>>1; } else break; } } void pop(){ swap(h[1],h[size]); size--; int now=1; int son=now<<1; while(son<=size){ if((son|1)<=size&&cmp(h[son|1],h[son]))son|=1; if(cmp(h[son],h[now])){ swap(h[son],h[now]); now=son; son=now<<1; } else break; } } bool cmp(const int a,const int b){//用于控制小根堆还是大根堆 return a>b; } int top(){ return h[1]; } bool empty(){ return size==0; } }heap; set<int>go[mx]; int ans[mx]; int num; int ind[mx];//每个点入度 int main(){ int T=Read(); while(T--){ num=0; n=Read(),m=Read(); for(int i=1;i<=m;++i){ int u=Read(),v=Read(); go[v].insert(u); } for(int i=1;i<=n;++i){ for(set<int>::iterator it=go[i].begin();it!=go[i].end();++it){ ind[*it]++; } } for(int i=1;i<=n;++i){ if(!ind[i]){ heap.push(i); } } while(!heap.empty()){ int u=heap.top(); heap.pop(); ans[++num]=u; if(go[u].empty())continue; set<int>::iterator it=go[u].end(); do{ --it; int v=*it; ind[v]--; if(!ind[v])heap.push(v); }while(it!=go[u].begin()); } if(num!=n){ printf("Impossible!\n"); } else{ for(int i=n;i>=1;--i){ printf("%d ",ans[i]); } printf("\n"); } //清空数据 memset(ind,0,sizeof(ind)); memset(ans,0,sizeof(ans)); for(int i=1;i<=n;++i)go[i].clear();//清空边 } return 0; }