题目描述
Windy 有 N 个不同重量的球,从 1 个单位到 N 个单位。现在,他尝试以 1 到 N 的方式标记它们:
没有两个球共享相同的标签。
标签满足几个约束条件,例如“标有 a 的球比标有 b 的球轻”。
你能帮助windy找到解决办法吗?
输入
输入的第一行是测试用例的数量。每个测试用例的第一行包含两个整数,N (1 ≤ N ≤ 200) 和 M (0 ≤ M ≤ 40,000)。接下来的 M 行每行包含两个整数 a 和 b,表示标有 a 的球必须比标有 b 的球轻。 (1 ≤ a, b ≤ N) 每个测试用例前有一个空行。
输出
对于单行输出的每个测试用例,球的权重从标签 1 到标签 N。如果存在多个解决方案,则应输出标签 1 权重最小的一个,然后标签 2 权重最小,然后标签 3 的最小权重等等…如果不存在解决方案,则输出 -1。
输入样例:
5
4 0
4 1
1 1
4 2
1 2
2 1
4 1
2 1
4 1
3 2
输出样例
1 2 3 4
-1
-1
2 1 3 4
1 3 2 4
思路: 本题的意思是,给出N个标签球,标签球之间的大小关系已经给出,我们要做的是按照重量的大小给每一个标签球进行标号,重量大的标号也就大,如果某一次标号中多个球都满足标号的条件,那么按照球号大的进行先标号,标号也是1–N. 如果中间存在了环也就无法进行标号,那么解就不存在.
样例:
在样例中,先对3号求进行标号:label[3] = 5,然后删除3号球的两条边,这时1,2号都可进行标号,那么由于2号求号更大,所以先进行label[2] = 4,之后1,4号也可进行标号,同样先label[4] = 3,然后label[1] = 2,label[5] = 1; 在标号过程中,我们是按照出度为0的球进行标号.
由于题中没说明,则可能会出现重边,在构建时,如果边已经存在了,入度就不需要进行+1了.否则会出现,有的顶点其实没有入度边了,但入度数组中该大小还不为0.
解题思路:
-
建立原图的逆向图,这样在每次进行标号时,我们是依据入度为0进行搞的.由于中间可能出现多个球都可进行标号,所以我们不能再使用栈了,我们改用使用一个
for(int i = n; i >= 1; i--)
在这个过程中我们先要寻找入度为0的球且是最大标号的那个,然后进行标号,从而进行下一轮.如果不能找到,则说明该图中存在环,结束即可. 对于每一次循环都会进行一次标号. -
采用正向图,但处理的时候是根据出度为0的点进行标号的,同时该点的所有出度进行-1,之后的思路和上个思路相同.
参考代码
#include<iostream>
#include<string.h>
using namespace std;
const int maxn = 210;
int map[maxn][maxn],label[maxn],in[maxn],n,m,t,flag,T;
void TopoSort(){
flag = 0;
for(int i = n; i >= 1; i--){//采用循环来进行标号.
//寻找入度为0的最大标号的球.
t = 0;//用于存找到的入度为0的最大编号.
for(int j = n; j>=1; j--){//从标号最大开始找,找到的第一个即是最大的那个.
if(!in[j]){
t = j;
break;
}
}
if(!t){//如果没有找到则说明有环.
flag = 1;
break;
}
in[t] = -1;//入度改变,防止影响下一次入度为0节点的寻找.
label[t] = i;//进行标号
for(int j = 1; j <= n; j++){//该点所有临界点的入度进行-1
if(map[t][j]){
in[j]--;
}
}
}
}
int main()
{
int u,v;
cin>>T;
while(T--){
//初始化
memset(map,0,sizeof(map));
memset(label,0,sizeof(label));
memset(in,0,sizeof(in));
cin>>n>>m;
for(int i = 1; i <= m; i++){
cin>>u>>v;
if(!map[v][u]) {//如果已经读入过了就不再进行读入,防止重边影响Topo排序.
map[v][u] = 1;
in[u]++;
}
}
TopoSort();
if(flag){//有环
cout<<"-1"<<endl;
}else{
for(int i = 1; i <= n-1; i++){//标号成功则进行输出.
cout<<label[i]<<" ";
}
cout<<label[n]<<endl;
}
}
return 0;
}