AcWing 4576. 素数独立集(详细解释)

这篇具有很好参考价值的文章主要介绍了AcWing 4576. 素数独立集(详细解释)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

题意

就是给你一个长度为n的集合(保证没有重复的数出现),需要你去构成一个最大长度的子集,且这个子集里面的数都没有一种题目给定的关系,关系就是在这个子集中没有任何一个元素是另一个元素的素数倍数。比如a%b = 0, k = a b k = \frac{a}{b} k=bak不能是素数。{2,8,17} 是素数独立集,而 {2,8,16}或 {3,6} 不是素数独立集因为 16 8 = 2 , 6 3 = 2 \frac{16}{8} = 2,\frac{6}{3}=2 816=2,36=2

思路

最开始想法非常简单就是对于一个数而言我就看这n个数当中有多少个是它的素数倍数然后n减去这些数的个数。
伪代码:

for(int i = 1; i <= n; i ++)
{
	for(int j = arr[i]; j < N; j += arr[i])
	{
		if(j / arr[i] == primes) cnt++;
	}
	ans = max(ans, n-cnt);
}

当然肯定是不对的,就对于样例来说2 4 8 16 32,对于2这个数来说只有4不满足,意味着我可以把8 16 32和2放在一个集合,不用往后面说了。我是傻X。

思来想去最后还是看了题解,对于本题我将结合看到的题解和一些额外的知识理一下自己的思路。

独立集

首先我们要清楚什么是独立集:就是一个点集(也可以是其他的集合),点集中的各点没有关系。

最大独立集

那么就是点的个数最多的独立集。
那么对于最大独立集有一个很重要的性质就是:最大独立集 = 点的总数 - 点的最大匹配 (最小点覆盖) 其实这也很好去理解,对于本题的最大匹配也就是所有有关系的匹配,也就是存在素数倍数的两个数。

这里简单说明一下二分图和最大匹配吧,想了解详细点的话自行CSDN吧。

二分图

一张图中(一般都是无向图,但是对于一些有向图也会有二分图的性质),全部的点分为两个集合,对于每个集合中点与点之间是没有边连接的。那么所有的边存在两个集合之间的。

最大匹配

对于最大匹配来说,就是对于你找到的所有边,任意两条边没有公共顶点。

分析

那么对于本题来说首先我们先来分析,什么情况下才有可能存在素数倍数的说法,如果对于两个数a, b如果a有奇数个质因子,b有偶数个质因子那么显然这两个数才会有可能会产生素数倍数的说法。因为一个素数因子数为奇数的数a只有乘上两个素数才能成为一个新的素数因子为奇数的数b,显然a与b不存在冲突关系,素数因子数为偶数的两个数同理。所以我们可以根据每个数的素数因子个数来划分集合,因为拥有相同奇偶性的数是不会有素数倍数的产生,这也是本题的关键所在。 具有相同奇偶性的数在一个集合。然后我们可以将有素数倍数的两个数建边。然后按照我们上面的讲述,我们建完二分图过后求一个最大匹配就完美解决了。但是通常我们求最大匹配的算法是匈牙利算法 ( O ( n ∗ e ) ) (O(n*e)) (O(ne))n个点e条边,对于本题来说很显然会超时的。那么我们这儿将会引用一种更好的算法 HK算法 O ( ( n ) ∗ e ) O(\sqrt(n)*e) O(( n)e) 对于本算法笔者也是当一个黑盒使用。文章来源地址https://www.toymoban.com/news/detail-623526.html

代码

#include<bits/stdc++.h>

#define IOS ios::sync_with_stdio(false);cin.tie(nullptr)
#define endl "\n"
#define xx first
#define yy second

using namespace std;

const int N = 5e4 + 5, M = 6e5+ 5;
const int inf = 0x3f3f3f3f;

struct Edge
{
    int to, next;
}ns[M];
 
bool isnp[M], vst[N];;
int prime_num[M], prm[M];
int n, cur, top, dis;
int Mx[N], My[N], Nx, Ny, dx[N], dy[N], Ax[N], Ay[N];
int head[N], ext[M];

void cal_prime_number() // 求素数因子的个数
{
    int res;
    prime_num[1] = 0;
    for (int i = 2; i < M; ++i)
    {
        int t = i;
        res = 0;
        for (int j = 2; j * j <= t; ++j)
        {
            while (t % j == 0)
            {
                ++res;
                t /= j;
            }
        }
        if (t != 1) ++res;
        prime_num[i] = res;
    }
}
 
void get_prime(int u) // 求素数因子的种类
{
    top = 0;
    for (int i = 2; i * i <= u; ++i)
    {
        if (u % i == 0)
        {
            prm[top++] = i;
            while (u % i == 0)
                u /= i;
        }
    }
    if (u != 1) prm[top++] = u;
}
 
bool searchP() //HK算法
{
    queue<int> Q;
    dis = inf;
    memset(dx, -1, sizeof dx);
    memset(dy, -1, sizeof dy);
    for (int i = 1; i <= Nx; ++i)
    {
        if (Mx[i] == -1)
        {
            Q.push(i);
            dx[i] = 0;
        }
    }
    while (!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        if (dx[u] > dis) break;
        for (int i = head[u]; i != -1; i = ns[i].next)
        {
            int v = ns[i].to;
            if (dy[v] == -1)
            {
                dy[v] = dx[u] + 1;
                if (My[v] == -1) dis = dy[v];
                else
                {
                    dx[My[v]] = dy[v] + 1;
                    Q.push(My[v]);
                }
            }
        }
    }
    return dis != inf;
}
 
bool DFS(int u)//HK算法
{
    for (int i = head[u]; i != -1; i = ns[i].next)
    {
        int v = ns[i].to;
        if (!vst[v] && dy[v] == dx[u] + 1)
        {
            vst[v] = 1;
            if (My[v] != -1 && dy[v] == dis) continue;
            if (My[v] == -1 || DFS(My[v]))
            {
                My[v] = u;
                Mx[u] = v;
                return true;
            }
        }
    }
    return false;
}
 
int Match()//HK算法
{
    int res = 0;
    memset(Mx, -1, sizeof Mx);
    memset(My, -1, sizeof My);
    while (true)
    {
        memset(vst, 0 , sizeof vst);
        queue<int> Q;
        dis = inf;
        memset(dx, -1, sizeof dx);
        memset(dy, -1, sizeof dy);
        for (int i = 1; i <= Nx; ++i)
        {
            if (Mx[i] == -1)
            {
                Q.push(i);
                dx[i] = 0;
            }
        }
        while (!Q.empty())
        {
            int u = Q.front();
            Q.pop();
            if (dx[u] > dis) break;
            for (int i = head[u]; i != -1; i = ns[i].next)
            {
                int v = ns[i].to;
                if (dy[v] == -1)
                {
                    dy[v] = dx[u] + 1;
                    if (My[v] == -1) dis = dy[v];
                    else
                    {
                        dx[My[v]] = dy[v] + 1;
                        Q.push(My[v]);
                    }
                }
            }
        }
        if (dis == inf) break;
        for (int i = 1; i <= Nx; ++i)
        {
            if (Mx[i] == -1 && DFS(i))
                ++res;
        }
    }
    return res;
}
 
void add_edge(int u, int v) //建边
{
    ns[cur].next = head[u];
    ns[cur].to = v;
    head[u] = cur;
    ++cur;
}

void solve(int op)
{
    cur = 0;
    memset(head, -1, sizeof head);
    memset(ext, 0, sizeof ext);
    Nx = Ny = 0;
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        int x;
        cin >> x;
        if (prime_num[x] & 1) //划分集合
        {
            Ax[++Nx] = x;
            ext[x] = Nx;
        }
        else
        {
            Ay[++Ny] = x;
            ext[x] = Ny;
        }
    }
    for (int i = 1; i <= Nx; ++i)//建边
    {
        get_prime(Ax[i]);
        for (int j = 0; j < top; ++j)
        {
            int goal = Ax[i] / prm[j];
            int index = ext[goal]; 
            if (index == 0) continue;
            add_edge(i, index);
        }
    }
    for (int i = 1; i <= Ny; ++i)//建边
    {
        get_prime(Ay[i]);
        for (int j = 0; j < top; ++j)
        {
            int goal = Ay[i] / prm[j];
            int index = ext[goal];
            if (index == 0) continue;
            add_edge(index, i);
        }
    }
    cout << "Case " << op << ": " << n - Match() <<  endl;
}

signed main()
{
    cal_prime_number();
    int _;
    cin >> _;
    for(int i = 1; i <= _; i ++) solve(i);
    return 0;
}

到了这里,关于AcWing 4576. 素数独立集(详细解释)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法】远方来信,从数学表达式算法到汇编语法解释器

    在繁华的都市中,小悦作为一名软件工程师,每天都在这座钢筋水泥的森林里忙碌。她的生活似乎被工作和各种琐碎的事情填满了,但在这个繁忙的生活中,她总能在工作之余找到一些小小的乐趣。 这天下班后,小悦收到了一封来自国外同学苏菲的email。邮件的内容让她的思

    2024年02月05日
    浏览(41)
  • 数据结构—串的详细解释(含KMP算法)

    1.1串的定义 串:串是由零个或多个字符组成的有限序列,又叫字符串(其的存储结构包含顺序表存储、单链表存储的形式。) 一般记为s=\\\"a1a2a3....an\\\"(n=0),其中,s是串的名称,用双引号(也可以使用单引号)括起来的字符序列是串的值,注意引号不是串的内容。ai(i=i=n)可以是字母、

    2023年04月09日
    浏览(45)
  • TD算法超详细解释,一篇文章看透彻!

    郑重声明:本系列内容来源 赵世钰(Shiyu Zhao)教授的强化学习数学原理系列,本推文出于非商业目的分享个人学习笔记和心得。如有侵权,将删除帖子。原文链接:https://github.com/MathFoundationRL/Book-Mathmatical-Foundation-of-Reinforcement-Learning 上一节我们讲到, Robbins-Monro Algorithm 算法解

    2024年02月01日
    浏览(51)
  • acwing蓝桥杯 - 数学知识【下】

     目录 欧拉函数 快速幂 求组合数 I 博弈论         Nim游戏  在数论,对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目,记作φ(n).  φ(1)=1 1、分解质因子,求出质因子p 2、将p带入,套公式 为了代码方便,套第三个公式  补充: 若a与m互质 ,则有  大佬算法讲

    2023年04月15日
    浏览(28)
  • 动力学约束下的运动规划算法——Hybrid A*算法(附程序实现及详细解释)

       前言(推荐读一下)    本文主要介绍动力学约束下的运动规划算法中非常经典的Hybrid A*算法,大致分为三部分,第一部分是在传统A * 算法的基础上,对Hybrid A * 算法的原理、流程进行理论介绍。第二部分是详细分析 MotionPlanning运动规划库中Hybrid A * 算法的源码,进一

    2024年02月08日
    浏览(59)
  • 【聚类算法】带你轻松搞懂K-means聚类(含代码以及详细解释)

    聚类是一个将数据集中 在某些方面相似的数据成员 进行分类组织的过程,聚类就是一种发现这种内在结构的技术,聚类技术经常被称为 无监督学习 。 k均值聚类是最著名的划分聚类算法,由于简洁和效率使得他成为所有聚类算法中最广泛使用的。给定一个数据点集合和需要

    2024年02月01日
    浏览(37)
  • 【数据挖掘】决策树归纳中ID3算法讲解及构建决策树实战(图文解释 超详细)

    需要完整PPT请点赞关注收藏后评论区留言私信~~~ 分类是一种重要的数据分析形式。数据分类也称为监督学习,包括学习阶段(构建分类模型)和分类阶段(使用模型预测给定数据的类标号)两个阶段。数据分类方法主要有决策树归纳、贝叶斯分类、K-近邻分类、支持向量机

    2023年04月09日
    浏览(46)
  • 【云计算与大数据技术】数据编码LZSS算法、Snappy压缩库及分布式通信系统的讲解(图文解释 超详细)

    数据编码概述 - 在分布式系统中需要处理大量的网络数据,为了加快网络数据的传输速度,通常需 要对传输数据进行编码压缩 数据压缩是以尽可能少的数码来表示信源所发出的信号,减少容纳给定的消息集合或数据采样集合的信号空间,这里讲的信号空间就是被压缩的对象,是

    2024年02月16日
    浏览(114)
  • 【python】算法设计:回文素数

    2024年02月13日
    浏览(40)
  • 超级详细用C语言判断一个数是否是素数

    先上代码: #include stdio.h int main() {         int n,i;     printf(\\\"请输入一个数: \\\");     scanf(\\\"%d\\\",n);     for(i=2;in;i++){         if(n%i==0){             break;         }     }     if(n==i){         printf(\\\"是素数\\\");     }     else         printf(\\\"不是素数\\\"); } 理解: 素数

    2024年02月08日
    浏览(92)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包