首页 > 试题广场 >

带权的DAG节点排序

[编程题]带权的DAG节点排序
  • 热度指数:534 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解

DAG即Directed Acyclic Graph,有向无环图.用DAG可以描述一些有依赖关系的任务组,而这些任务还有另外一个属性,即都有一个权重,标示这个任务的重要性.

我们需要你来实现一个算法,对DAG里面的节点进行排序,保证排序不违背DAG的依赖关系,即一个任务A如果排在任务B前面,那么在DAG中不能存在由B到A的路径.另外一个要求就是,让权重大的任务尽量优先执行.


输入描述:
在第一行给定DAG的节点数n和边数e.

后面n行,每一行是 节点的 标号和权重, seq weight.

最后e行,每一行是对于边的描述, s t.


输出描述:
排序好的节点标号,在一行内输出,空格隔开.
示例1

输入

4 4
1 2
2 3
3 5
4 4
1 2
1 3
2 4
3 4

输出

1 3 2 4
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.PriorityQueue;

class Vertex {
    int lable; //标号
    int weight; //权值
    int in; //入度
    ArrayList<Vertex> connectList; //该顶点所指向的点
    Vertex(int lable, int weight) {
        this.lable = lable;
        this.weight = weight;
        this.connectList = new ArrayList<Vertex>();
    }
}

/**
 * 拓扑排序
 * 
 * @author wylu
 */
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] strs = br.readLine().split(" ");
        int n = Integer.parseInt(strs[0]), e = Integer.parseInt(strs[1]);

        //建图
        HashMap<Integer, Vertex> dag = new HashMap<Integer, Vertex>();
        for (int i = 0; i < n; i++) {
            strs = br.readLine().split(" ");
            int lable = Integer.parseInt(strs[0]), weight = Integer.parseInt(strs[1]);
            dag.put(lable, new Vertex(lable, weight));
        }
        for (int i = 0; i < e; i++) {
            strs = br.readLine().split(" ");
            int from = Integer.parseInt(strs[0]), to = Integer.parseInt(strs[1]);
            dag.get(from).connectList.add(dag.get(to));
            dag.get(to).in++;
        }

        ArrayList<Integer> res = topologicalSort(dag);
        for (int i = 0; i < res.size() - 1; i++) {
            System.out.print(res.get(i) + " ");
        }
        System.out.println(res.get(res.size() - 1));
    }

    private static ArrayList<Integer> topologicalSort(HashMap<Integer, Vertex> dag) {
        ArrayList<Integer> res = new ArrayList<>();

        //大顶堆
        PriorityQueue<Vertex> queue = new PriorityQueue<>(new Comparator<Vertex>() {
            @Override
            public int compare(Vertex o1, Vertex o2) {
                return -Integer.compare(o1.weight, o2.weight);
            }
        });

        //入度为0的点入队
        for (Integer lable : dag.keySet()) {
            if (dag.get(lable).in == 0) queue.offer(dag.get(lable));
        }

        while (!queue.isEmpty()) {
            Vertex fromV = queue.poll();
            res.add(fromV.lable);

            for (Vertex toV : dag.get(fromV.lable).connectList) {
                toV.in--; //邻接点入度减1
                if (toV.in == 0) queue.offer(toV);
            }
        }
        return res;
    }
}

编辑于 2019-01-18 17:00:08 回复(1)
这题只有一个测试用例也太过分了吧。在节点信息输入时先用map记录每个节点的权重,然后在边信息输入时,在source节点上累加上target节点的权重。
拓扑排序,对节点的权重进行降序排列后输出即可。
n, e = map(int, input().split())
vertex = dict()
for _ in range(n):
    v, w = map(int, input().strip().split())
    vertex[v] = w
for _ in range(e):
    start, end = map(int, input().strip().split())
    vertex[start] += vertex[end]
for node, _ in sorted(vertex.items(), key=lambda x: -x[1]):
    print(node, end=' ')

发表于 2021-04-10 19:10:40 回复(0)
#include <bits/stdc++.h>
using namespace std;

struct P{
    int u, w;
    friend bool operator > (P p1, P p2){
        return p1.w < p2.w;
    }
};

int main(){
    int n, m, u, v, w;
    scanf("%d%d", &n, &m);
    vector<P> node(n+1);
    vector<int> d(n+1, 0);
    vector<bool> vis(n+1, false);
    vector<int> edge[n+1];
    vector<int> r;
    for(int i=0;i<n;i++){
        scanf("%d%d", &u, &w);
        node[i+1] = {u, w};
    }
    for(int i=0;i<m;i++){
        scanf("%d%d", &u, &v);
        edge[u].push_back(v);
        d[v]++;
    }
    priority_queue<P, vector<P>, greater<P> > q;
    for(int i=1;i<=n;i++)
        if(d[i] == 0){
            q.push(node[i]);
            vis[i] = true;
        }
    while(!q.empty()){
        int t = q.size();
        while(t--){
            P p = q.top();
            q.pop();
            u = p.u;
            w = p.w;
            for(auto &v: edge[u])
                d[v]--;
            r.push_back(u);
        }
        for(int i=1;i<=n;i++)
            if(d[i]==0 && !vis[i]){
                q.push(node[i]);
                vis[i] = true;
            }
    }
    for(int i=0;i<n;i++)
        printf("%d ", r[i]);
    return 0;
}

发表于 2020-11-25 19:55:27 回复(0)
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

int main(void) {
  int n, e;
  cin >> n >> e;
  vector<int> weights(n + 1);
  
  int seq, w;
  for (int i = 0; i < n; ++i) {
    cin >> seq >> w;
    weights[seq] = w;
  }
  
  int u, v;
  vector<vector<int>> g(n + 1);
  vector<int> indegrees(n + 1, 0);
  for (int i = 0; i < e; ++i) {
    cin >> u >> v;
    g[u].emplace_back(v);
    ++indegrees[v];
  }
  
  priority_queue<pair<int, int>> q; // maximum heap
  for (int u = 1; u <= n; ++u) 
    if (!indegrees[u]) q.emplace(weights[u], u);
  
  while (not q.empty()) {
    const auto [weight, curr] = q.top(); q.pop();
    cout << curr << ' ';
    for (const int nxt : g[curr])
      if (!--indegrees[nxt]) q.emplace(weights[nxt], nxt);
  }
  
  return 0;
}

发表于 2021-07-25 19:27:09 回复(0)
       这个题目如果理解了的话思路挺简单的,就是实际操作起来有点复杂(可能是我太新手了)。整体的思路就是拓扑排序,依次把入度为0的节点从图中删除,如果有多个点同时入度为0,则比较权值,优先删除权值大的节点。
       所以代码数据结构的实现上需要两个数组(或一个二维数组)分别保存每个节点的入度和权值,需要一个map保存节点的拓扑关系,注意节点的出度可能大于1。除此之外还需要另一个map保存出度为0的节点,每次输出其中权值最大的一个(这里还可以考虑使用优先队列实现)。每次输出(删除)一个节点时要把它的下一跳节点入度-1,把入度为0的节点加入输出map,循环即可。
//图的拓扑排序,权值大的优先
//首先输出入度为0的,入度为0的有多个比较权值
//数组记录每个点的权值
//map记录点的拓扑关系
import java.util.Scanner;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;

public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int e = in.nextInt();
        int []weight = new int[n+1];
        int []inDegree = new int[n+1];
        Map<Integer,ArrayList<Integer>> map = new HashMap<>();
        for(int i=1;i<=n;i++){
            int t = in.nextInt();
            weight[t] = in.nextInt();
        }
        for(int i=0;i<e;i++){
            int tmp1 = in.nextInt();
            int tmp2 = in.nextInt();
            inDegree[tmp2]++;
            if(map.containsKey(tmp1))    map.get(tmp1).add(tmp2);
            else {
                map.put(tmp1,new ArrayList<Integer>());
                map.get(tmp1).add(tmp2);
            }
        }
        //首先遍历树的入度,将入度为0的点,加入输出队列
        Map<Integer,Integer> print = new HashMap<>();
        for(int i=1;i<=n;i++){
            if(inDegree[i]==0){
                print.put(i,weight[i]);
            }
        }
        //输出print中权值最大的节点
        StringBuilder builder = new StringBuilder();
        while(!print.isEmpty()){
            int pos = getPos(print);
            builder.append(pos);
            builder.append(" ");
            print.remove(pos);
            //对pos的下一跳处理
            ArrayList<Integer> list = map.get(pos);
            if(list!=null){
                for(int i:list){
                    inDegree[i]-=1;
                    if(inDegree[i]==0){
                        print.put(i,weight[i]);
                    }
                }
            }
        }
        builder.deleteCharAt(builder.length()-1);
        System.out.println(builder.toString());
    }
    public static int getPos(Map<Integer,Integer> print){
        int pos = 0,max = 0;
        for(int key:print.keySet()){
            if(print.get(key)>max){
                max = print.get(key);
                pos = key;
            }
        }
        return pos;
    }
}



发表于 2021-04-05 20:06:48 回复(0)
C++ 
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;

struct cmp
{
    bool operator()(pair<int,int>& a, pair<int,int>& b)
    {
        return a.second < b.second;
    }
};

int main()
{
    int n,a,b,e;
    cin >> n >> e;
    vector<pair<int,int>> node(n + 1);
    vector<int> degree(n + 1,0);
    vector<bool> vis(n + 1,false);
    vector<vector<int>>edge(n + 1);
    for(int i = 0; i < n; i++)
    {
        cin >> a >> b;
        node[i + 1] = {a,b};
    }
    vector<int> res;
    for(int i = 0; i < n; i++)
    {
        cin >> a >> b;
        edge[a].push_back(b);
        degree[b]++;
        
    }
    priority_queue<pair<int,int>,vector<pair<int,int>>, cmp> pq;
    for(int i = 1; i <= n; i++)
    {
        if(degree[i] == 0) pq.push(node[i]),vis[i] = true;
    }
    
    while(pq.size())
    {
        
        int x = pq.size();
        while(x--)
        {
            auto tmp = pq.top();
            pq.pop();
            int first = tmp.first;
            int second = tmp.second;
            for(int i = 0; i < edge[first].size(); i++)
            {
                degree[edge[first][i]]--;
            }
                res.push_back(tmp.first);
        }
        for(int i = 1; i <= n; i++)
        {
            if(degree[i] == 0 && !vis[i]) pq.push(node[i]),vis[i] =true;
        }
        
    }
    for(int i = 0 ; i < n; i++)
    {
        cout << res[i] << " ";
    }
    cout << endl;
}









发表于 2020-07-03 17:27:04 回复(0)