题目描述
有N个球,重量从1到N,各不相同,每个球有个编号也是从1到N,各不相同。现在给你一些约束条件,每个约束条件给出数字A,B,表示A号球轻于B号球。请你求出满足约束条件的某个球的重量的全排列,注意如果有多个排列满足条件,我们希望1号球的重量越小越好;当1号球的重量一样时,希望2号球的重量越小越好; 当2号球的重量一样时,希望3号球的重量越小越好......
输入
第一行给出数字N,M,代表有N个球,M个约束条件N (1 ≤ N ≤ 200) , M (0 ≤ M ≤ 40,000).下面将有M行,每行两个数字A,B,(1 ≤ a, b ≤ N)表示A号球轻于B号球。
输出
依次输出从1号球到N号球的重量,任两个数字之间以一个空格格开,无解时输出-1。
样例输入
复制
【样例1】 4 1 3 2 【样例2】 6 3 2 4 4 3 4 6
样例输出
复制
【样例1】 1 3 2 4 【样例2】 1 2 4 3 5 6
1.拓扑排序(Topological Sorting)
是对**有向无环图(DAG)**的顶点进行线性排序,使得对于图中的每一条有向边 u → v
,顶点 u
都排在顶点 v
的前面。
(上次的苹果也是拓扑排序)
2. 算法步骤(Kahn算法)
-
初始化:
-
计算每个顶点的入度(指向它的边的数量)。
-
将入度为
0
的顶点加入队列(或堆)。
-
-
处理队列:
-
取出队首顶点
u
,加入拓扑序。priority_queue -
遍历
u
的所有邻接顶点v
:-
将
v
的入度减1
。 -
若
v
的入度变为0
,将其加入队列。
-
-
-
检查环:
- 若拓扑序的顶点数 ≠ 总顶点数,则图中存在环,无拓扑序。
5. 变种:字典序最小拓扑序
-
最小堆 :每次选编号最小的入度为
0
的顶点。 -
最大堆 :每次选编号最大的入度为
0
的顶点(用于反向图)。
"重量序列" 指的是按重量从小到大的顺序排列的球的编号 。例如,若重量 1 对应 3 号球,重量 2 对应 1 号球,重量 3 对应 2 号球,则重量序列为 [3,1,2]
(即重量 1 是 3 号,重量 2 是 1 号,重量 3 是 2 号)。
"字典序最小" 指的是这个序列在字典排序中最小(类似字典中单词的排序)。例如 [1,2,3]
比 [1,3,2]
小,因为第二个位置 2 < 3。
如果用正向拓扑排序(小顶堆选最小编号),得到的是重量序列的字典序最小 。此时++优先保证 "重量从小到大对应的球编号尽可能小",但无法保证 "1 号球的重量尽可能小"。++
"球编号序列" 指的是按球的编号顺序(1→2→...→N)排列的重量 。例如,1 号球的重量是 2,2 号球的重量是 3,3 号球的重量是 1,则编号序列的重量为 [2,3,1]
(即 1 号重 2,2 号重 3,3 号重 1)。
"球编号序列的重量最小" 指的是按编号顺序,每个位置的重量尽可能小(先保证 1 号最小,再保证 2 号最小,以此类推),这正是题目要求的目标。
deepsleep了
代码
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
int n,m,a,b,ind[210],c[210][210]{0};
int main(){
cin>>n>>m;
vector<vector<int>>adj(n+1);
for(int i=0;i<m;++i){
cin>>a>>b;
if(c[a][b])continue;
c[a][b]=1;
adj[b].push_back(a);
ind[a]++;
}
priority_queue<int>who;
for(int i=1;i<=n;++i){
if(ind[i]==0)who.push(i);
}
vector<int>ans;
while(!who.empty()){
int x=who.top();
ans.push_back(x);
who.pop();
for(auto i:adj[x]){
ind[i]--;
if(ind[i]==0){
who.push(i);
}
}
}
if(ans.size()!=n){
cout<<"-1";return 0;
}
int w[210]{0},x=n;
for(auto i:ans){
w[i]=x--;
}
for(int i=1;i<=n;++i){
cout<<w[i]<<" ";
}
return 0;
}