[逆向拓扑排序]POJ3687 Labeling Balls

时间:2022-12-01 23:19:54

看了几页书上的网络流感觉不想看了,找了前面几章的题目打算水一水练手,结果碰上这个题也是很囧。

一开始做是正向,从轻到重建边,记录入度,搞优先队列让小序号的先出队,同时搞一个普通队列把每次选的入度为0的点记录下来,结果一发WA。

后来仔细看了看题目,结果要输出的是结点的标号。

比如最开始第一个选的入度为0的点是5,那么我的第一个答案就是5了,其实题目不是这个意思,第一个答案应该是1第几个被选出队,2第几个被选出队。

然而。。我竟然把样例过了。。

好吧。把记录答案的普通队列改成了数组,记录结点第几个出队。

结果又是一发WA。

想水题不认真就是这个下场。

其实这题应该反向建边,然后逆序拓扑,给序号大的标上大标号,因为正向搞其实是在做序号的贪心,我觉得也是可以的,但是思维和代码都比逆向难。

具体见代码和注释。

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
int map[205][205];
int in[205];
int ans[205];
int main(){
int T;
scanf("%d",&T);
while(T--){
memset(in,0,sizeof(in));
memset(map,0,sizeof(map));
memset(ans,0,sizeof(ans));

int n, m, a, b;
scanf("%d%d", &n, &m);
for(int i = 0; i < m; ++i)
{
scanf("%d%d", &a, &b);
if( !map[b][a])
{
map[b][a] = 1; //重到轻建边
in[a] += 1;
}
}

priority_queue<int>q; //默认是优先把序号大的出列 因为是逆向建图 需要给序号的标上大的标号
int num = n, cnt = num;
for(int i = n; i>=1; --i) if( !in[i]) q.push( i);
while( !q.empty())
{
int u=q.top(); q.pop(); num-=1;
for(int i = 1;i <= n; ++i)
{
if(map[u][i] && ( (--in[i]) == 0)) q.push( i);
}
ans[u] = cnt-- ; //给序号大的附上大标号
}
if(num != 0) { puts("-1"); }
else {
for(int i = 1; i <= n; ++i) printf("%d ", ans[i]);
puts("");
}
}
}