思路
题目意思很明朗,但是坑属实有点大,而且我到现在也没想清楚为什么是反向建图,反向建图为什么能够AC我没找到一个合理的解释。最开始就是直接正向建图通过优先队列的小根堆强行拓扑排序输出完事,交了很多发都是WA。实在想不到错误原因,好在看了一组测试数据才明白为啥会错误。
input:
1
3 1
3 1
answer:
3 1 2
错误答案
2 3 1
错误的理由很简单,题目要求有钱人先站在前面(编号小)。3站在1的前面之后,虽然没有其他约束条件但是有"隐含条件"的限制啊,2应该排在1的后面。也就是形成312的序列。如果采用小根堆维护实际上第一次访问队头元素就是2了,直接跨越了1 - 2的约束条件。但是为什么反向建图 + 大根堆 + 逆序输出能AC?其实大根堆就已经确保了序号大先输出,转成逆序输出就在后面了。但是给不了足够的证明过程,留一个小坑吧~
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <set>
#include <queue>
using namespace std;
const int maxn = 3e4+5;
const int maxm = 1e5+5;
struct edge{
int to;
int next;
}e[maxm];
vector<int>v;
priority_queue<int>q; //默认大根堆
int head[maxn];
int s[maxn];
int cnt,n,m;
inline void clear_set()
{
cnt = 0;
memset(head,-1,sizeof(head));
memset(s,0,sizeof(s));
while(!q.empty()) q.pop();
v.clear();
}
inline void addedge(int x,int y)
{
e[cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt++;
}
inline void topsort()
{
for(int i = 1;i <= n;i++){
if(s[i] == 0){
q.push(i);
}
}
while(!q.empty()){
int p = q.top();
v.push_back(p);
q.pop();
for(int i = head[p];~i;i = e[i].next){
if(--s[e[i].to] == 0){
q.push(e[i].to);
}
}
}
for(int i = n-1;i > 0;i--){
printf("%d ",v[i]);
}
printf("%d\n",v[0]);
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
clear_set();
scanf("%d%d",&n,&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
addedge(y,x);s[x]++;
}
topsort();
}
return 0;
}
愿你走出半生,归来仍是少年~