hihocoder 1158 质数相关 (二分图最大独立集 最大流ISAP求解)

91 篇文章 1 订阅
78 篇文章 0 订阅
#1158 : 质数相关
时间限制:2000ms
单点时限:1000ms
内存限制:256MB

描述

两个数a和 b (a<b)被称为质数相关,是指a × p = b,这里p是一个质数。一个集合S被称为质数相关,是指S中存在两个质数相关的数,否则称S为质数无关。如{2, 8, 17}质数无关,但{2, 8, 16}, {3, 6}质数相关。现在给定一个集合S,问S的所有质数无关子集中,最大的子集的大小。
输入

第一行为一个数T,为数据组数。之后每组数据包含两行。

第一行为N,为集合S的大小。第二行为N个整数,表示集合内的数。
输出

对于每组数据输出一行,形如"Case #X: Y"。X为数据编号,从1开始,Y为最大的子集的大小。
数据范围

1 ≤ T ≤ 20

集合S内的数两两不同且范围在1到500000之间。

小数据

1 ≤ N ≤ 15

大数据

1 ≤ N ≤ 1000
样例输入

    3
    5
    2 4 8 16 32
    5
    2 3 4 6 9
    3
    1 2 3

样例输出

    Case #1: 3
    Case #2: 3

    Case #3: 2


题目链接:http://hihocoder.com/problemset/problem/1158


题目分析:求二分图最大独立集,点权-最大匹配(网络流最小割)=最大独立集

建图方法:把素数分解时素因子都为奇数的数作为二分图的一边,另一边为左边数字质数相关的数

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
int const MAX = 500005;
int const MAXN = 3000005;
int const INF = 0x7fffffff;
int p[MAX], a[1005];
bool prime[MAX];
int gap[MAXN], pre[MAXN], head[MAXN], d[MAXN], cur[MAXN];
int e_cnt, n;
int src, sink;

void get_prime()  
{  
    memset(prime, true, sizeof(prime));  
    prime[1] = false;  
    for(int i = 2; i <= sqrt(500000); i++)    
        if(prime[i])    
            for(int j = i + i; j <= 500000; j += i)    
                prime[j] = 0;  
}

struct EDGE
{
    int to, cap, flow, next;
}e[MAXN];

void Add_Edge(int u, int v, int cap)
{
    e[e_cnt].to = v;
    e[e_cnt].cap = cap;
    e[e_cnt].flow = 0;
    e[e_cnt].next = head[u];
    head[u] = e_cnt ++;

    e[e_cnt].to = u;
    e[e_cnt].cap = 0;
    e[e_cnt].flow = 0;
    e[e_cnt].next = head[v];
    head[v] = e_cnt ++;
}

void BFS(int t)
{
    queue <int> Q;
    memset(gap, 0, sizeof(gap));
    memset(d, -1, sizeof(d));
    d[t] = 0;
    Q.push(t);
    while(!Q.empty())
    {
        int v = Q.front();
        Q.pop();
        gap[d[v]] ++;
        for(int i = head[v]; i != -1; i = e[i].next)
        {
            int u = e[i].to;
            if(d[u] == -1)
            {
                d[u] = d[v] + 1;
                Q.push(u);
            }
        }
    }
}

int ISAP(int s, int t)
{
    BFS(t);
    int ans = 0, u = s, flow = INF;
    memcpy(cur, head, sizeof(cur));
    while(d[s] < e_cnt)
    {
        int i = cur[u];
        for(; i != - 1; i = e[i].next)
        {
            int v = e[i].to;
            if(e[i].cap > e[i].flow && d[u] == d[v] + 1)
            {
                u = v;
                pre[v] = i;
                flow = min(flow, e[i].cap - e[i].flow);
                if(u == t)
                {
                    while(u != s)
                    {
                        int j = pre[u];
                        e[j].flow += flow;
                        e[j ^ 1].flow -= flow;
                        u = e[j ^ 1].to;
                    }
                    ans += flow;
                    flow = INF;
                }
                break;
            }
        }
        if(i == -1)
        {
            if(-- gap[d[u]] == 0)
                break;
            int mi = e_cnt - 1;
            cur[u] = head[u];
            for(int j = head[u]; j != -1; j = e[j].next)
                if(e[j].cap > e[j].flow)
                    mi = min(mi, d[e[j].to]);
            d[u] = mi + 1;
            gap[d[u]] ++;
            if(u != s)
                u = e[pre[u] ^ 1].to;
        }
    }
    return ans;
}

int main()
{   
    get_prime();
    p[1] = 0;
    for(int i = 1; i <= 500000; i++)
        for(int j = 1; j <= 500000 / i; j++)
            if(prime[j])
                p[i * j] = 1 - p[i];
    int T;
    scanf("%d", &T);
    for(int ca = 1; ca <= T; ca++)
    {
        e_cnt = 0;
        memset(head, -1, sizeof(head));
        printf("Case #%d: ", ca);
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        src = 0;
        sink = n + 1;
        for(int i = 1; i <= n; i++)
        {
            if(p[a[i]])
                Add_Edge(src, i, 1);
            else
                Add_Edge(i, sink, 1);
        }
        for(int i = 1; i <= n; i++)
        {
            if(p[a[i]])
            {
                for(int j = 1; j <= n; j++)
                {
                    if(!p[a[j]])
                    {
                        if(a[i] > a[j])
                        {
                            if(a[i] % a[j] == 0 && prime[a[i] / a[j]])
                                Add_Edge(i, j, 1);
                        }
                        else
                        {
                            if(a[j] % a[i] == 0 && prime[a[j] / a[i]])
                                Add_Edge(i, j, 1);
                        }
                    }
                }
            }
        }
        printf("%d\n", n - ISAP(src, sink));
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值