zoukankan      html  css  js  c++  java
  • LightOJ1356 最大独立集 HK算法 素数分解

    解析

    当a和b满足(a = b imes prime)时,我们说a,b有冲突关系,将所有数看成是图中的顶点,那么a和b有冲突关系,就在a,b之间连一条边。题目是:给定一些数,从这些数中选出一些数组成一个集合,使这个集合中的每两个数都没有冲突关系,也就是每两个顶点之间都没有边,问这个集合最多可以有多少个数。

    任意一个整数可以分解为多个素数因子的乘积,而只有分解后,素数因子数为奇数和偶数的两个数才有可能有冲突,因为一个素数因子数为奇数的数a只有乘上两个素数才能成为一个新的素数因子为奇数的数b,显然a与b不存在冲突关系,素数因子数为偶数的两个数同理。所以我们可以根据素数因子数的奇偶来将所有数划分为两个集合X和Y,X中的元素和Y中的元素可能有边,而同一个集合中的元素没有边,显然这是一个二分图。

    再看这个问题,其实就是求最大独立集的元素个数。最大独立集就是选取最多的点组成一个集合,使任意所选两点均不相连。

    有这么一个公式:最大独立集的元素个数 = 顶点数 - 最大匹配数。这里的最大匹配数就是二分图的最大匹配。所以我们只需要找出上述二分图的最大匹配数,用顶点数减去最大匹配数就是答案。注意二分图顶点的个数多达4e4,用匈牙利算法会严重超时,我们使用优化的算法——Hopcroft-Krap算法,邻接表存图时,时间复杂度是(O(Vsqrt{E}))。HK算法可以参考我的另一篇文章《求二分图最大匹配——Hopcroft-Krap算法》。

    求最大独立集还有另一种求法,是求这个图的补图的最大团,但是这种方法时间复杂度很高,我们不采用这种方法解这道题

    AC代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 4e4 + 5;
    const int maxm = 5e5 + 5;
    const int inf = 0x3f3f3f3f;
    
    struct Edge
    {
        int to, next;
    };
    
    bool isnp[maxm];
    int prime_num[maxm];
    int prm[maxm];
    int n;
    int Mx[maxn], My[maxn], Nx, Ny;
    int dx[maxn], dy[maxn], dis;
    bool vst[maxn];
    int Ax[maxn], Ay[maxn];
    int top;
    int head[maxn];
    Edge ns[maxm];
    int ext[maxm];
    int cur;
    
    void cal_prime_number()
    {
        int res;
        prime_num[1] = 0;
        for (int i = 2; i < maxm; ++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()
    {
        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)
    {
    	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 MaxMatch()
    {
        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;
    }
    
    int main()
    {
        cal_prime_number();
        int t;
        scanf("%d", &t);
        int inp;
        for (int cas = 1; cas <= t; ++cas)
        {
            cur = 0;
            memset(head, -1, sizeof head);
            memset(ext, 0, sizeof ext);
            Nx = Ny = 0;
            scanf("%d", &n);
            for (int i = 1; i <= n; ++i)
            {
                scanf("%d", &inp);
                if (prime_num[inp] & 1)
                {
                    Ax[++Nx] = inp;
                    ext[inp] = Nx;
                }
                else
                {
                    Ay[++Ny] = inp;
                    ext[inp] = 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);
                }
            }
            printf("Case %d: %d
    ", cas, n - MaxMatch());
        }
        return 0;
    }
    /*
    3
    5
    2 4 8 16 32
    5
    2 3 4 6 9
    3
    1 2 3
    
    */
    
    
  • 相关阅读:
    那些年,我们一起做过的 Java 课后练习题(66 70)
    UI自动化测试:App的WebView页面中,当搜索栏无搜索按钮时处理方法
    追剧《大秦帝国》之感
    雷达距离方程 理解
    观影<和平战士> 之后感
    【转】使用blend改变图片颜色
    ios UITabBar/UITabBarController
    Windows批处理开启/停止服务及隐藏批处理窗口
    Windows设置定时自动重启
    Windows使用命令行查看文件的hash值(certutil)
  • 原文地址:https://www.cnblogs.com/lucianosimon/p/9417031.html
Copyright © 2011-2022 走看看