拓扑排序是将偏序关系变化成线性关系的一种方法
如何进行拓扑排序?我们介绍 Kahn 算法
A
kahn算法的主要思想是不断删除入度为0的节点并将其移除 具体步骤:
- 统计每个节点的入度
- 将所有入度为0的点加入队列
- 不断的从队列中取出节点
- 将其加入结果序列
- 对其所有出边的目标节点入度减1
- 如目标节点入度为0,则入队
- 如果最终结果包括所有节点,则说明排序完成,否则说明图中有环
struct Kahn
{
vint order;
i64 n;
Kahn(const vvint &g)
{
this->n = g.size();
order.reserve(n);
kahnsort(g);
}
void kahnsort(const vvint &g)
{
queue<i64> qu;
vint indeg(n);
for (i64 u = 1; u <= n; u++) {
for (auto &&v : g[u]) {
indeg[v]++;
}
}
for (i64 i = 1; i <= n; i++) {
if (indeg[i] == 0) qu.push(i);
}
while (!qu.empty()) {
i64 u = qu.front();
order.emplace_back(u);
for (auto &&v : g[u]) {
if (--indeg[v] == 0) qu.push(v);
}
}
}
vint get_order() const
{
return order;
}
};对于这题,我们先疏通题意
题目给定我们一个树,数上每个边都关联两个贡献值,对于边 定义边的贡献值为:
其中需要我们如下定义,给定一个排列,将中每个数填入树中的节点上
我们需要确定一种排列,使整个树的贡献值最大
[!思路] 显然,因为我们获得的是一个排列,我们可以确定一种关系,让每一个边都可以获得较大的一个数,这实际上确定了一种偏序关系
所以我们便可以使用拓扑排序来确定线性关系,具体如下:
我们对树上每一个边从较小的节点向较大的节点建立有向边,最终形成有向图,最后我们对这个有向图拓扑排序即可
AC solution
void solve()
{
i64 n;
cin >> n;
vint p(n + 1, 0);
vector<vector<i64>> adj(n + 1);
for (i64 i = 1; i < n; i++) {
i64 u, v, x, y;
cin >> u >> v >> x >> y;
if (x > y) {
adj[v].push_back(u);
p[u]++;
}
else {
adj[u].emplace_back(v);
p[v]++;
}
}
// 拓扑排序
queue<i64> qu;
for (i64 i = 1; i <= n; i++) {
if (p[i] == 0) qu.push(i);
}
vint order;
order.reserve(n);
while (!qu.empty()) {
i64 u = qu.front();
qu.pop();
order.push_back(u);
for (auto &&i : adj[u]) {
p[i]--;
if (p[i] == 0) {
qu.push(i);
}
}
}
vint res(n + 1);
for (i64 i = 0; i < order.size(); i++) {
res[order[i]] = i + 1;
}
for (i64 i = 1; i <= n; i++) {
cout << res[i] << " ";
}
cout << endl;
}