本题坑点
题目没有给数据规模,我去别的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;
} 


京公网安备 11010502036488号