这道题看上去就知道是拓扑排序,再一看题,应该是求字典序最小的合法方案,所以贪心的找当前入度为0的编号最小的点就行了。
恭喜,你掉到坑里了。
其实我一开始就是这么做的,后来在测样例第三组数据的时候炸掉了,这组数据就可以卡掉这个贪心 (这样例还真良心啊) 。
我们不妨反着考虑,最后一位放合法的最大值(设为 x x x)显然是最优的,因为这样可以让小于 x x x的数更加往前,而大于 x x x的数因为 x x x的位置已经确定了所以在哪都没有影响。填完最后一位后,再考虑倒数第二位、倒数第三位……最后到第一位,我们就会发现,答案反序过来就是字典序最大的序列,所以我们建立反图,然后贪心的找字典序最大的拓扑序列,再反向输出就好了。
贴代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=100010;
struct node
{
int cnt,id;
friend bool operator<(const node &x,const node &y)
{ if(x.cnt==y.cnt)return x.id<y.id;return x.cnt>y.cnt;}
//因为用了结构体所以要重载运算符
}a[N];
int t,m,n,top,ans[N],h[N],pre[N],to[N];
bool vis[N];
priority_queue<node>q;
void ins(int u,int v)
{
pre[++top]=h[u];h[u]=top;to[top]=v;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);ans[0]=top=0;bool f=true;
memset(vis,false,sizeof(vis));memset(h,0,sizeof(h));
while(!q.empty())q.pop();
for(int i=1;i<=n;i++)a[i].cnt=0,a[i].id=i;//初始化一些要用的数组和优先队列
while(m--)
{
int x,y;scanf("%d%d",&x,&y);
ins(y,x);a[x].cnt++;//反向建边,统计入度
}
for(int i=1;i<=n;i++)q.push(a[i]);
while(!q.empty())
{
int x=q.top().id;q.pop();
if(vis[x])continue;
if(a[x].cnt>0){ f=false;break;}
//如果当前入度最小的点的入度大于0,说明无解
vis[x]=true;ans[++ans[0]]=x;
for(int i=h[x];i;i=pre[i])
{
int v=to[i];
a[v].cnt--;q.push(a[v]);
}
}
if(!f)printf("Impossible!\n");
else
{
for(int i=n;i>1;i--)printf("%d ",ans[i]);
printf("%d\n",ans[1]);
}//反向输出
}
return 0;
}