【并查集】判环

题意梳理

  1. (n* n) 棋盘,m 个车,初始无两车同行同列;
  2. 单次操作:移动一个车任意横竖距离,移动后仍不能同行同列;
  3. 目标:让所有车放到主对角线 (i,i),求最小移动次数;
  4. 关键:(m<n),棋盘存在空行空列。

Problem - 1411C - Codeforces

一、为什么要把题目转换成图结构

  1. 原始棋盘限制 所有车不会同行、同列,也就是说每一个列数字只出现一次,每一个行数字只出现一次。 把列编号、行编号全部当成普通数字点,一辆车坐标 (x,y),就代表数字 x 和数字 y 存在绑定关系。

  2. 连通块等价含义 如果两个数字在同一个连通块里,代表块内所有行列上的车,依靠题目允许的移动规则,可以自由调换位置,没有永久冲突。 我们最终目标是让数字 k 的列匹配数字 k 的行,也就是位置 (k,k)。

  3. 两种连通块带来不同代价

  • 链型块:块里点的数量 > 块里车的数量 没有互相卡死的情况,每一辆不在对角线上的车,只需要移动一次就能归位,没有额外开销。
  • 环型块:块里点的数量 = 块里车的数量 环里所有车辆互相占用对方需要的行 / 列,直接移动一定会冲突。题目保证 m<n,棋盘存在空行列,必须先挪一辆车去空位中转,每一个环额外多消耗一次操作。
  1. 特殊车辆处理 如果一辆车坐标 (x,x),本身就在主对角线上,不需要移动,不参与建图、不计入移动总量。

二、文字符号定义(纯文本,可直接复制)

  1. R:全部车辆集合,每个元素是一对数字 (x,y) cnt:满足 x≠y 的车辆总数量,每一台基础消耗 1 次移动

R(x≠y) = cnt

  1. G:仅由 x≠y 的车辆构建的无向图 S:图 G 里任意一个连通块 |S|:连通块 S 包含的数字点总数 E (S):连通块 S 内部包含的车辆(边)总数

  2. 判断一个连通块是环的条件 E (S) > 0 并且 |S| == E (S)

  3. loop:图 G 中所有满足上面条件的环连通块总个数

  4. 最终答案计算式 ans = cnt + loop

三、并查集数组对应含义

fa u:数字 u 在并查集中的父节点 siz rt:根为 rt 的连通块,包含的点总数,对应 | S| cntE rt:根为 rt 的连通块,包含的有效车辆数量,对应 E (S) vis rt:标记该连通块根是否已经统计过,防止重复计算环

复制代码
#include <cstdio>
using namespace std;
const int MAX = 100010;
int fa[MAX], siz[MAX], cntE[MAX];
bool vis[MAX];

int find(int x) {
    if (fa[x] != x) fa[x] = find(fa[x]);
    return fa[x];
}

int main() {
    int t; scanf("%d", &t);
    while (t--) {
        int n, m; scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i) {
            fa[i] = i, siz[i] = 1, cntE[i] = 0, vis[i] = 0;
        }
        int cnt = 0;
        for (int i = 0; i < m; ++i) {
            int x, y; scanf("%d%d", &x, &y);
            if (x == y) continue;
            cnt++;
            int fx = find(x), fy = find(y);
            if (fx != fy) {
                fa[fy] = fx;
                siz[fx] += siz[fy];
                cntE[fx] += cntE[fy];
            }
            cntE[fx]++;
        }
        int loop = 0;
        for (int i = 1; i <= n; ++i) {
            int rt = find(i);
            if (!vis[rt]) {
                vis[rt] = 1;
                if (cntE[rt] && siz[rt] == cntE[rt]) loop++;
            }
        }
        printf("%d\n", cnt + loop);
    }
    return 0;
}
相关推荐
涉密IT资质笔记几秒前
涉密资质投入产出分析模型:时间成本、四块成本构成与 ROI 决策框架
java·开发语言·算法
木井巳3 分钟前
【BFS 解决拓扑排序】火星词典
java·算法·leetcode·广度优先·宽度优先·图搜索算法
fpcc3 分钟前
c++编程实践—thread_local变量的应用
c++
Xudde.5 分钟前
Mr-Robot 靶机渗透测试(Vulnhub Writeup)
笔记·学习·安全·web安全
北风toto14 分钟前
KMP 算法详解:从暴力匹配到 O(n+m) 的字符串搜索
java·开发语言·算法
AOI小白新手上路14 分钟前
DRSformer Rain200H 省钱推理复现笔记
笔记
j7~17 分钟前
【仿 RabbitMQ 项目】发布订阅消息队列 (篇九):队列数据管理,内存镜像 + SQLite 持久化实现
数据库·c++·分布式·sqlite·rabbitmq·队列数据管理
dtq042419 分钟前
C++ - 变量
开发语言·c++
傻啦嘿哟21 分钟前
天气数据爬虫:基于Scrapy爬取全国天气,做可视化大屏
c++·爬虫·scrapy
林澈在路上21 分钟前
哪个写歌软件可以用自己声音?MELO音乐详解
人工智能·python·算法