CSP 201512-4 送货

简介: CSP 201512-4 送货

问题描述

image.png

输入格式

image.png

输出格式

image.png

数据范围

image.png

输入样例1:

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

输出样例1:

1 2 4 1 3 4

样例1解释

城市的地图和小明的路径如下图所示。

image.png

输入样例2:

4 6
1 2
1 3
1 4
2 4
3 4
2 3

输出样例2:

-1

样例2解释

城市的地图如下图所示,不存在满足条件的路径。

image.png

本题链接http://118.190.20.162/view.page?gpid=T34

AC代码

C++

#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int N = 10010, M = 100010;
int n, m;
set<int> g[N];
int p[N];
int ans[M], top;
int find(int x)      // 并查集
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
void dfs(int u)
{
    while (g[u].size())
    {
        int t = *g[u].begin();
        g[u].erase(t), g[t].erase(u);  // 删边
        dfs(t);
    }
    ans[ ++ top] = u;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) p[i] = i;   // 初始化p数组
    while (m -- )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        g[a].insert(b), g[b].insert(a);
        p[find(a)] = find(b);
    }
    int s = 0;
    for (int i = 1; i <= n; i ++ )
        if (find(i) != find(1))      // 不是连通图
        {
            puts("-1");
            return 0;
        }
        else if (g[i].size() % 2) s ++ ; // s记录的是度数为奇数的点的
    // 度数为奇数的点不是0个或2个,或者度数为奇数的点为2个但是起点的度数不是奇数,则不符合欧拉路径
    if (s != 0 && s != 2 || s == 2 && g[1].size() % 2 == 0)
    {
        puts("-1");
        return 0;
    }
    dfs(1);    // 开始遍历欧拉路径
    for (int i = top; i; i -- )   // 欧拉路径实际上记录的是倒序,故求正向欧拉路径需要逆序输出
        printf("%d ", ans[i]);
    return 0;
}

java

注:java版代码只有50分

import java.util.*;
public class Main {
    final int N = 100010;
    int n, m;
    int[] ans = new int[N];
    int top;
    int[] p = new int[10010];
    TreeSet[] g = new TreeSet[10010];
    void run() {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        for (int i = 0; i <= n; i++) {
            g[i] = new TreeSet();
        }
        for (int i = 1; i <= n; i++) {
            p[i] = i;
        }
        while (m -- != 0) {
            int a, b;
            a = sc.nextInt();
            b = sc.nextInt();
            g[a].add(b);
            g[b].add(a);
            p[find(a)] = find(b);
        }
        int s = 0;
        for (int i = 1; i <= n; i++) {
            if (find(i) != find(1)) {
                System.out.println("-1");
                return;
            } else if (g[i].size() % 2 != 0)  {
                s++;
            }
        }
        if (s != 0 && s != 2 || (s == 2 && g[1].size() % 2 == 0)) {
            System.out.println("-1");
            return;
        }
        dfs(1);
        for (int i = top; i != 0; i--) {
            System.out.printf("%d ", ans[i]);
        }
    }
    void dfs(int u) {
        while (g[u].size() != 0) {
            int t = (Integer) g[u].first();
            g[u].remove(t);
            g[t].remove(u);
            dfs(t);
        }
        ans[ ++ top] = u;
    }
    int find(int x) {
        if (p[x] != x) {
            p[x] = find(p[x]);
        }
        return p[x];
    }
    public static void main(String[] args) {
        new Main().run();
    }
}

代码解释

从一个点出发,不重不漏的经过图中每一条边的一条路径(允许多次经过同一个点)。

从一个点出发,不重不漏的经过图中每一条边的一条路径(允许多次经过同一个点)。如果此路径的起点和终点相同,则称其为一条欧拉回路。

入度(indegree)就是有向图中指向这个点的边的数量,即有向图的某个顶点作为终点的次数和

出度(outdegree)就是从这个点出去的边的数量,即有向图的某个顶点作为起点的次数和

一个点的度(degree)指图中与该点相连的边数

无向连通图

欧拉回路:所有点的度数都为偶数即为有欧拉回路

欧拉路径:度数为奇数的点要么有2个要么有0个

若为0个那么从任意点出发都可以,如果有2个,那么起点必须是其中一个点,终点必须为另一个点


有向连通图

欧拉回路:所有点的入度等于出度

欧拉路径:1.所有点的入度等于出度 2.有一个点(终点)的入度=出度+1,且存在一个点(起点)的出度=入度+1


从当前点出发,只要有出去的边就往外走,深度优先遍历,回溯路径就是它的一个方案

为什么从起点搜到终点不是一个方案?

因为在搜索的过程中,不一定会把所有的边全部走到(有一堆额外的环),但因为起点和终点是唯一的,

且其余点的度数均为偶数,故我们在回溯的过程中把其他的结点(那些没有走过的环)补充到搜索图中即可

字典序最小:从当前点出去搜的时候,按照编号从小到大搜索一遍,就可以保证字典序最小


欧拉路径证明(无向图)

对于起点而言,设其度数为n,那么起始往出走需要一条边,其余到达起点的话,就必然是进来一次出去一次,故起点的度数必须是奇数

对于终点而言,设其度数为m,那么最终到达需要一条边,其余到达终点的点,需要再次出去,故终点的度数必须是奇数

对于非起点和非终点的其余点,它们只是作为中间的“桥梁”,故到达这些点后还需要离开,故这些点的度数必须是偶数

如果起点和终点是同一个点,那么对于起点(终点)而言,出去需要一条边,最终到达该点需要一条边,其余时候进来必然还要出去,

故在这种情况下,起点(终点)的度为偶数

综上所述,对于一个无向图的欧拉路径而言,度数为奇数的点要么为2个要么为0个,且为2个的时候,这两个点分别做起点和终点

    dfs(u)   // 求得的序列其实是欧拉路径的倒序序列
    {
        for u的所有边
            dfs()  // 扩展
        seq += u   // 把u加到序列中
    }

欧拉路径dfs和一般图论dfs区别:

一般图论dfs用点来判重,时间复杂度在O(n+m)

欧拉路径是用边来判重,如果用一个bool变量来表示每个边是否被搜过,时间复杂度会很高:

举个例子,对于一个点,假设其有m条自环,那么我们在遍历的过程中搜第一条边,搜完之后又回到该点,

此时第一条边已经被用过了,故我们要跳过这一条边搜第二条边,依次类推,在搜第三条边的时候需要跳过第一条边和第二条边

以此类推,在搜完m条边的时候就会跳m^2次,这样对于1e5的数据量显然就会超时

处理方法:每用一条边不是进行简单的标记,而是直接进行删除,这样就可以降为线性时间复杂度

无向图的删除方法:因为建无向图对于每一条边会建立两次(不同方向),故我们在删除的时候还需要对它的反向边进行一次标记

如何根据一条边找到它的反向边:因为建边的时候是按照(0,1),(2,3)…进行建边,故求反向边可直接根据 i^1 求得

上述代码中并未采取这种建边方式,直接把边放入set中,set自然有序,且易直接删边


目录
相关文章
|
算法 Go C++
CSP 202203-2 出行计划
CSP 202203-2 出行计划
390 0
CSP 202203-2 出行计划
|
搜索推荐 数据安全/隐私保护 开发者
欧盟 DMA 法案泄露:苹果或将被迫对 Siri、App Store、浏览器等服务做彻底整改
欧盟 DMA 法案泄露:苹果或将被迫对 Siri、App Store、浏览器等服务做彻底整改
217 0
欧盟 DMA 法案泄露:苹果或将被迫对 Siri、App Store、浏览器等服务做彻底整改
|
人工智能 BI Go
csp 4-1 买菜 (300 分)
csp 4-1 买菜 (300 分)
125 0
csp 4-1 买菜 (300 分)
|
Web App开发 存储 移动开发
决战Cookie僵尸:恶性追踪API意在“提高警惕”
对抗目前僵尸Cookie——好像永远不会丢失你数据的cookie——的战争硝烟四起,因为越来越多的用户知道了其技术内幕。尽管广泛传播的Flash cookie和最近最新的HTML5存储技术内幕让用户提高了警惕,但要我们避免Internet用户被顽固跟踪,仍然任重而道远。
1326 0
|
架构师 Apache
美国国家冰球联盟(NHL)的问题追踪、文档管理和单点登录
国家冰球联盟(NHL)是世界公认的、最专业的冰球联盟。我最近注意到,他们正愉快地使用我们的工具,而且他们并不介意谈论他们是如何使用这些工具的。
1411 0
|
新零售 安全 数据安全/隐私保护
金融安全资讯精选 2017年第十三期 百慕大离岸律师事务所遭黑客攻击,Google 发布HTTPS 普及度报告,Bad Rabbit攻击预警和安全建议,PCI SSC 发布新的 3DS 支付标准
百慕大离岸律师事务所遭黑客攻击,Google 发布HTTPS 普及度报告,Bad Rabbit攻击预警和安全建议,PCI SSC 发布新的 3DS 支付标准
2203 0