CF840B Leha and another game about graph 题解(证明 dfs 求解的正确性 + 详细注释代码)

news/2024/7/15 18:59:31 标签: 深度优先, 算法, 图论, c++

题意

给定 n n n 个点, m m m 条边,保证没有自环,且图连通。对于每个点有一个期望度数 d [ i ] d[i] d[i] − 1 ≤ d [ i ] ≤ 1 -1\leq d[i]\leq1 1d[i]1。询问是否能保存图中的一些边使得所有点的度数的奇偶性与期望度数的奇偶性相同。(当 d [ i ] = − 1 d[i] = -1 d[i]=1 时点 i i i 的度数为任意都可以)。

思路

首先需要知道:
(1)因为增加一条边,图的总度数就会增加 2 2 2,因此图中的总度数总是为偶数。所以当图中点的 期望度数和 为奇数时 且 没有 d [ i ] = − 1 d[i] = -1 d[i]=1 的点时无解。
(2)而有 d [ i ] = − 1 d[i] = -1 d[i]=1 的点时我们可以通过调整与 − 1 -1 1 的点的连边来调整图中点的度数,当删去一条与 − 1 -1 1 点的连边时, − 1 -1 1 的点的度数变化可以不用考虑,以此达到调整度数的目的。

考虑使用 dfs,因为保证图是连通的,所以从任意根节点向下搜(当总度数为奇数时从 d [ i ] = − 1 d[i] = -1 d[i]=1 的点开始搜),每次保证除了根以外的子树所有顶点都符合 d d d 度数要求,而最终返回到整棵树的根时,所有点的期望度数已经符合。

使用dfs的正确性证明(返回到根时根的度数期望也被满足的证明):
当期望总度数为奇数时,我们以 − 1 -1 1 节点为根,那么返回到根时根的度数已经无关重要了。
当期望总度数为偶数时,
1.以奇数度数期望的节点为根,那么除了根节点以外其他点的期望度数被满足时,度数之和肯定也是奇数,我们在 dfs 过程中是以增加边来调节度数的,所以总度数肯定为偶数,那么说明肯定有奇数条边连接在了根上,所以根的期望度数为奇数也得到了保证。
2.以偶数度数期望的节点为根,同理当调整完其余度数后图的总度数为偶数,除根以外的所有点度数也为偶数,根的度数自然也为偶数。

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int d[N];
struct edge{
    int to, nex;
}e[N * 2];
int head[N], tot = 1, cnt;
void add(int to, int from){
    e[++ tot].to = to;
    e[tot].nex = head[from];
    head[from] = tot;
}
bool Edge[N * 2];
int vis[N], d[N], n, m;

int dfs(int u){
    /*
    res 代表自己期望度数奇偶性,若是增加的边的数量与之奇偶性相同,那么res = 0, 否则 res = 1
    所以 res 是用来说明自身的期望奇偶性有没有满足的。
    初始没有边,所以偶数的期望度数 = 0 也代表着最开始就被满足,奇数也代表不满足
    */
    int res = d[u]; 
    for(int i = head[u]; i; i = e[i].nex){
        int v = e[i].to;
        if(vis[v]) continue ;
        if(dfs(v)) {
            Edge[i] = Edge[i ^ 1] = 1; //增加该边标记
            res ^= 1; //增加一条边 期望度数是否被满足发生改变
        }
    }
    if(d[u] == -1) res = 0;//-1 的期望度数一定被满足
    return res;
}
int main(){
    ios::sync_with_stdio(false);
    cout.tie(NULL);
    cin >> n >> m;
    int id = 0, sum = 0;
    for(int i = 1; i <= n; i ++){
        cin >> d[i];
        if(d[i] == -1) id = i;
        else sum ^= d[i];
    }

    for(int i = 1; i <= m; i ++){
        int u, v;
        cin >> u >> v; 
        add(u, v);
        add(v, u);
    }
    
    if((sum & 1) && !id){
        cout << "-1";
        return 0;
    }

    dfs(max(1, id)); // -1 点存在就都从 -1 点开始

    cout << cnt << "\n";
    for(int i = 2; i <= 2 * m + 1; i += 2){
        if(Edge[i]) cout << (i / 2) << "\n";
    }
    return 0;
}

http://www.niftyadmin.cn/n/160078.html

相关文章

yolov5 训练自定义数据集全过程

1、下载YOLOv5代码并安装环境 git clone https://gitee.com/monkeycc/yolov5.git cd yolov5 pip install -r requirements.txt # install2、下载完yolov5之后在和yolov5同级的目录下创建一个叫datasets的目录 目录结构如下&#xff1a; 3、准备数据集&#xff1a; 3.1 数据集…

PPT设计资源汇总

PPT设计资源汇总 时间&#xff1a;2023-3-16 版次&#xff1a;Version 1 编写人&#xff1a;PEZHANG 对已掌握的PPT设计资源进行了汇总梳理&#xff0c;部分链接加载可能需要外网&#xff0c;供参考&#xff0c;资源条目罗列如下&#xff1a; 图片素材网站&#xff08;4个图片…

MyBatis的Dao层实现方式

1.MyBatis的Dao层实现 1.1编写传统开发式 1.编写UserDao接口 public interface UserDao {List<User> findAll() throws IOException; } 2.编写UserDaoImpl实现 public class UserDaoImpl implements UserDao {public List<User> findAll() throws IOException {…

互联网医院开发|互联网医院申请需具备哪些条件?

这几年随着互联网技术的不断进步&#xff0c;医院也开始开展线上业务来带动线下业务&#xff0c;互联网医院就是依托于实体医院&#xff0c;为人们提供常规咨询和在线复诊等服务&#xff0c;虽然随着电子技术的发展&#xff0c;排队情况日渐好转&#xff0c;但去一次医院花大半…

28个案例问题分析---15---登陆之后我加入的课程调用接口报错--ArrayList线程不安全。占用内存情况

ArrayList线程不安全。占用内存情况故事背景方案&思路解决线程不安全的问题方案一&#xff1a;在这两个方法之前添加 synchronized 关键字。方案二&#xff1a;使用ThreadLocal变量。解决重复创建对象问题。总结&升华故事背景 存入redis的值&#xff0c;可能会出现错误…

[oeasy]python0110 屏幕点阵字体_3x5_5x7_雅达利字库

屏幕点阵字库 回忆上次内容 上次回顾了 字符字型 的 进化过程 从 谷腾堡 活字印刷中的 平织菱足体 到 文艺复兴中的 罗马正字 和 意大利斜体 再到 电传打字机 中的 teletype 字模 最终 发展到 点阵式 打字机Dot Matrix 显示器中的像素 可以构成点阵吗&#xff1f; 发光二极…

转战C#---day5

处理异常&#xff08;try-catch-finally&#xff09; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ConsoleApp10 {class Program{static void Main(string[] …

顺序表和链表的优缺点

这里说的链表是&#xff08;带头循环双向链表&#xff09;&#xff0c;下面我们就说一下他们的优缺点 首先顺序表 优点 1.支持随机访问 2.在O(1)的时间复杂度内&#xff0c;访问下标为i的位置。 3.很多算法的都必须用顺序表例如sort 4.缓存命中率高 缺点 1.每次容量满了…