zoukankan      html  css  js  c++  java
  • P7771 【模板】欧拉路径

    原题链接

    简要题意:

    给定 \(n\) 个点 \(m\) 条边的有向图,输出其字典序最小的欧拉路径。无解则输出 No.

    \(n \leq 10^5 , m \leq 2 * 10^5\).

    注:图中可能存在重边,自环。

    欧拉路径,即无重复无遗漏地经过所有边的一条路径。

    根据欧拉路径的定义,容易得到,如果一个有向图存在欧拉路径,则必须有:

    • 要么每个点的入度都等于出度,此时起点终点任意;要么除了两个点外的点入度等于出度,这两个点必有一个出度比入度大一,为唯一起点;另一个入度比出度大一,为唯一终点。

    于是我们可以成功判断无解情况,并对合法的情况,构造出起点和终点。

    构造路径的方法不难。每次都走当前点未走过的一条字典序最小的边,这样答案一定是最优的。因为字典序的特点是,一步优则全局优。

    注意到一个问题。我们可以对边进行排序(利用 vector 即可),然后按照顺序去走。同时用桶维护每个边是否被走过。

    然而此时,其实,时间复杂度是 \(\mathcal{O}(n + m^2)\).

    因为每次到一个点,都会把先前已经走过的边再次走一遍。这显然,会被卡掉。比如一个很简单的例子,\(n = 10^5 , m = 2 * 10^5\),其中有一半边是 \(1 \rightarrow 2\),另一半是 \(2 \rightarrow 1\),此时你就会因为扫边到 \(m^2\) 级别而导致超时。

    而问题的解决也比较容易想。每次开一个数组 st,记录我们应该从第几条边开始,每走一条就更新一下,这样每条边就真正只会被扫描一次,而不是仅仅走一次、扫描多次。

    时间复杂度:\(\mathcal{O}(n + m \log m)\).

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=2e5+1;
    
    int n,m,S,T,cnt=0;
    int in[N],out[N],st[N]; 
    vector<pair<int,int> > G[N];
    stack<int> s;
    bool h[N];
    
    inline void dfs(int x) {
    	for(int i=st[x];i<G[x].size();i=max(i+1,st[x])) {
    		int y = G[x][i].first , z = G[x][i].second;
    		if(h[z]) continue; h[z] = 1; st[x] = i + 1;
    		dfs(y);
    	} s.push(x);
    }
    
    int main() {
    	scanf("%d %d",&n,&m);
    	while(m--) {
    		int u,v;
    		scanf("%d %d",&u,&v);
    		out[u]++; in[v]++;
    		G[u].push_back(make_pair(v,++cnt));
    	}
    	int flag = 0;
    	for(int i=1;i<=n;i++) {
    		int p = in[i] - out[i];
    		if(p) flag++;
    		if(p == 1) S = i;
    		if(p == -1) T = i;
    	}
    	if(flag && flag!=2) return puts("No"),0;
    	if(flag == 0) S = T = 1;
    	if(!S || !T) return puts("No"),0;
    	for(int i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
    	dfs(T);
    	while(!s.empty()) {
    		printf("%d ",s.top());
    		s.pop();
    	}
    	return 0;
    }
    
    简易的代码胜过复杂的说教。
  • 相关阅读:
    Linux 设备驱动--- Poll 方法 --- Select【转】
    Linux 设备驱动--- 阻塞型字符设备驱动 --- O_NONBLOCK --- 非阻塞标志【转】
    一篇不错的v4l2入门文档【转】
    戴文的Linux内核专题:03 驱动程序【转】
    戴文的Linux内核专题:02 源代码【转】
    使用 Python 开始你的机器学习之旅【转】
    双缓冲(Double Buffer)原理和使用【转】
    内核中的内存申请:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages【转】
    几种常见的YUV格式--yuv422:yuv420【转】
    C程序编译过程浅析【转】
  • 原文地址:https://www.cnblogs.com/bifanwen/p/15755482.html
Copyright © 2011-2022 走看看