首页 > 试题广场 >

有序图

[编程题]有序图
  • 热度指数:1102 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 64M,其他语言128M
  • 算法知识视频讲解
现在给出一张含有 个点的有向无环图,我们称这张图是有序图当且仅当这个图满足以下条件:
1. 存在一个 1-n 数字的全排列 ,并令 号结点的权值为 p[i] 
2. 如果图中存在 号结点到 号结点的一条边,则 号结点的权值要小于 号结点的权值。 显然可能有多个序列满足条件,请你找出字典序最小的全排列 ,使得这个图成为有序图。
数据范围:

输入描述:
第一行包含两个正整数n,m,分别表示图上结点是数量和有向边的数量。 接下来m行每行有两个正整数u,v,表示存在一条从u结点到v结点的有向边。


输出描述:
输出一个字典序最小的,1-n的全排列,使得这张图是有序图,元素中间使用空格隔开。
示例1

输入

3 3
1 2
1 3
3 2

输出

1 3 2
#include <bits/stdc++.h>
using namespace std;

const int M = 100003;
vector<int> E[M];
int indegree[M];

int main(){
    int n, m, u, v;
    cin>>n>>m;
    int r[n+1];
    for(int i=0;i<m;i++){
        cin>>u>>v;
        E[v].push_back(u);
        indegree[u]++;
    }
    priority_queue<int> q;
    for(int i=1;i<=n;i++)
        if(!indegree[i])
            q.push(i);
    int k = n;
    while(!q.empty()){
        int p = q.top();
        q.pop();
        r[p] = k--;
        for(int i=0;i<E[p].size();i++){
            v = E[p][i];
            indegree[v]--;
            if(indegree[v]==0)
                q.push(v);
        }
    }
    for(int i=1;i<=n;i++){
        if(i==n)
            cout<<r[i]<<endl;
        else
            cout<<r[i]<<" ";
    }
    return 0;
}

发表于 2020-01-13 02:35:29 回复(1)
借鉴何止大佬的代码
import java.util.Scanner;
import java.util.ArrayList;
import java.util.PriorityQueue;


// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int n = in.nextInt();
            int m = in.nextInt();
            ArrayList<Integer> from[] = new ArrayList[n + 1]; // 保存1-n每个节点的前置节点
            int hash[] = new int[n + 1];  // 保存1-n每个节点的出度
            // 初始化from和hash
            for (int i = 0; i < m; i++) {
                int pre = in.nextInt();
                int next = in.nextInt();
                if (from[next] == null) from[next] = new ArrayList<>();
                from[next].add(pre);
                hash[pre]++;
            }
            // 最大优先队列
            PriorityQueue<Integer> q = new PriorityQueue<>(((o1, o2) -> o2 - o1));
            int w = n;  // 最大权重
            int ant[] = new int[n + 1]; // 1-n每个节点对应的权值数组
            // 下面是拓扑排序过程,顺便为每个节点赋权值
            for (int i = 1; i < n + 1; i++) {   
                // 将出度为0的节点添加到队列,(出度为0即最后一个节点)
                if (hash[i] == 0) {
                    q.add(i);
                }
            }
            while (!q.isEmpty()) {
                int t = q.poll();
                ant[t] = w; // 依次为每个节点赋权值
                w--;
                if (from[t] != null) {
                    for (int i : from[t]) {
                        hash[i]--;
                        if (hash[i] == 0) q.add(i);
                    }
                }
            }
            for (int i = 1; i < n + 1; i++) {
                System.out.print(ant[i] + " ");
            }
        }
    }
}


发表于 2023-10-31 17:19:41 回复(0)
逆向拓扑排序+优先队列,满足拓扑关系的前提下,将节点值大的往后放
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * @Author: coderjjp
 * @Date: 2020-05-11 22:27
 * @Description:
 * @version: 1.0
 */
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] line1 = br.readLine().split(" ");
        int n = Integer.parseInt(line1[0]);
        int m = Integer.parseInt(line1[1]);
        int hash[] = new int[n+1];//记录1-n的出度
        ArrayList<Integer> from[] = new ArrayList[n+1];//记录每个点被谁指向
        for (int i = 0; i < m; i++){
            String line[] = br.readLine().split(" ");
            int f = Integer.parseInt(line[0]);
            int t = Integer.parseInt(line[1]);
            if (from[t] == null)
                from[t] = new ArrayList<>();
            from[t].add(f);
            hash[f]++;
        }
        //维护一个最大优先队列
        PriorityQueue<Integer> queue = new PriorityQueue<>((o1, o2) -> o2 - o1);//将出度为0的进队
        for (int i = 1; i <= n; i++)
            if (hash[i] == 0)
                queue.offer(i);
        int ans[] = new int[n+1];//记录1-n个节点的权重
        int w = n;
        while (!queue.isEmpty()){
            int cur = queue.poll();
            ans[cur] = w;
            w--;
            if (from[cur] != null)
                for (int i: from[cur]){
                    hash[i]--;
                    if (hash[i] == 0)
                        queue.offer(i);
                }
        }
        StringBuilder res = new StringBuilder().append(ans[1]);
        for (int i = 2; i <= n; i++)
            res.append(" ").append(ans[i]);
        System.out.println(res);
    }
}


发表于 2020-05-11 22:43:30 回复(1)
1,先出出度为0的点作为最大值点。
2,同时存在多个出度为0的点,先出数值大的点。
from collections import defaultdict
import queue
graph,opp_graph,out = defaultdict(list),defaultdict(list),defaultdict(int)
n,m = map(int,input().split())

q = queue.PriorityQueue()
for _ in range(m):
    x,y = map(int,(input().split()))
    graph[x].append(y)
    opp_graph[y].append(x)
for d in range(1,n+1):
    out[d] = len(graph[d])
    if out[d]==0:
        q.put(-d)
ans ,count= [0 for i in range(n)],n
while not q.empty():
    node = -q.get()
    ans[node-1] = count
    count-=1
    for d in opp_graph[node]:
        out[d] -= 1
        if out[d]==0:
            q.put(-d)
print(' '.join(list(map(str,ans))))
    


发表于 2020-04-23 00:56:43 回复(8)
#include <bits/stdc++.h>

//freopen("temp.in","r",stdin);(1522)#include "utils.cpp"
using namespace std;

#define UF(i,start,end) for(auto i=start;i<end;i++)
(1523)#define DF(i,start,end) for(auto i=start;i>=end;i--)
int ri(){int a;scanf("%d",&a);return a;}//读取整数并返回
string rs(){string s;cin>>s;return s;}
typedef long long ll;
typedef vector<int> veci;
typedef vector<bool> vecb;
typedef vector<string> vecs;
typedef vector<vector<int>> vveci;

veci split(string s,char c){
    veci V;
    int i=0,j=1,n=s.length();
    while(i<n){
        while(j<n and s[j]!=c) j++;
        if(j==n-1) j++;
        V.push_back(atoi(s.substr(i,j-i).c_str()));
        i=j+1;
        j++;
    }
    return V;
}

template<typename T> ostream& operator<<(ostream& os,const vector<T>& v){
	for(auto t:v)
		os<<t<<" ";
	os<<endl;
	return os;
}
template <typename T>
ostream &print(ostream &os,const T &t){
	return os<<t<<endl;
}	
template <typename T,typename... Args>
ostream &print(ostream &os,const T &t,const Args&... rest){
	os << t <<" ";
	return print(os,rest...);
}	
template <typename T,typename... Args>
void PR(const T &t,const Args&... rest){
	print(cout,t,rest...);
}
template <typename T>
void reverse(T &t){
    reverse(t.begin(),t.end());
}
struct BigInteger{
    veci data;
    int size;
    BigInteger():size(0){}
    BigInteger(int val){
        size = 0;
        while(val){
            size++;
            data.push_back(val%10);
            val/=10;
        }
    }
   void multiply(BigInteger b){
        veci T(size,0);
        int temp = size;
        UF(i,0,b.size){
            UF(j,0,temp){
                if(j+i>=size){
                    size++;
                    T.push_back(data[j]*b.data[i]);
                }else
                    T[j+i]+=data[j]*b.data[i];
                 
             
            }
        }
         
        temp = size;
        UF(i,0,temp){
            if(T[i]>9){
                if(i==temp-1){
                    size++;
                    T.push_back(T[i]/10);
                }else T[i+1]+=T[i]/10;
                T[i]%=10;
            }
        }
        data.assign(T.begin(),T.end());
    }
    void print(){
        DF(i,size-1,0){
            cout<<data[i];
        }
        cout<<endl;
    }
    
};
 int random(int k){
	   return rand()%k+1;
   }
	vector<int> create_data(){
		PR(random(10),random(20));
	}
	
	void test(){
		unsigned seed = time(0);
		srand(seed);
		freopen("temp.out", "w", stdout);
		int N=5;//生成测试数据组数
		for(int i=0;i<N;i++)
			create_data();
	}
	
//拓扑
int MAX_N=100000;
vveci G(MAX_N);
int main(){
	//freopen("temp.in","r",stdin);
	//test();
    int n,m;
    cin>>n>>m;//写成模板输入
    int u,v;

/*
    拓扑排序+优先级队列
    参考了别人的代码才理解
    题目要求最小字典序,则要从后往前排没有出度的节点,为什么不从前往后呢,因为前
面的节点会受指向它的节点的排序影响 10 10 9 7 5 2 10 2 5 8 1 2 10 7 3 2 4 2 8 7 1 8 对应输出应该为: 1 6 2 3 4 7 10 8 9 5   */
    veci C(n+1,0);//出度
    
    priority_queue<int,vector<int>,less<int> > Q;//大的优先 从大到小出队列
    UF(i,0,m){
        cin>>u>>v;
        G[v].push_back(u);//G[v]  指向v的边 而不是v指向别人
        C[u]+=1;//从u点出的边个数
    }
    veci ans(n+1);
    int k=0;
    UF(i,1,n+1){
        if(C[i]==0) Q.push(i);//将出度为0的点入队列
    }
    int cnt=n;
    while(cnt>0){
        auto a=Q.top();
        Q.pop();
        ans[a]=cnt--;//让编号较小节点的保留更大的编号,先让后面编号大的又没有输出的点先排序
        for(auto b:G[a]){//将指向自身的边的出度-1
            C[b]--;
            if(C[b]==0){
                Q.push(b);//没有出度时入队列
            }
        }
        
    }
    UF(i,1,n)
        cout<<ans[i]<<" ";
    cout<<ans[n]<<endl;
	return 0;
	
}



发表于 2020-03-08 08:33:49 回复(0)
AOV图,输出拓扑排列
发表于 2019-10-09 07:46:47 回复(0)
//应该是拓扑排序,我理解错题意了吗?测试通过0%?
#include<bits/stdc++.h>
#define MAXN 100002
using namespace std;

int n,m,indegree[MAXN];
vector<int> adj[MAXN];

void toposort(){
    priority_queue<int,vector<int>,greater<int>> q;
    for(int i=1;i<=n;i++){
        if(indegree[i]==0){
            q.push(i);
        }
    }
    while(!q.empty()){
        int u=q.top();
        cout<<u<<" ";
        q.pop();
        for(int i=0;i<adj[u].size();i++){
            int v=adj[u][i];
            indegree[v]--;
            if(indegree[v]==0){
                q.push(v);
            }
        }
    }
}
int main(){
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        indegree[b]++;
        adj[a].push_back(b);
    }
    toposort();
    return 0;
}

用例:
10 10
9 7
5 2
10 2
5 8
1 2
10 7
3 2
4 2
8 7
1 8

对应输出应该为:

1 6 2 3 4 7 10 8 9 5

你的输出为:

1 3 4 5 6 8 9 10 2 7
发表于 2019-08-29 22:03:50 回复(3)