LightOJ1356 最大独立集 HK算法 素数分解

解析

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

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

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

有这么一个公式:最大独立集的元素个数 = 顶点数 - 最大匹配数。这里的最大匹配数就是二分图的最大匹配。所以我们只需要找出上述二分图的最大匹配数,用顶点数减去最大匹配数就是答案。注意二分图顶点的个数多达4e4,用匈牙利算法会严重超时,我们使用优化的算法——Hopcroft-Krap算法,邻接表存图时,时间复杂度是\(O(V\sqrt{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\n", cas, n - MaxMatch());
    }
    return 0;
}
/*
3
5
2 4 8 16 32
5
2 3 4 6 9
3
1 2 3

*/

posted @ 2018-08-04 00:20  LuSimon  阅读(640)  评论(0编辑  收藏  举报