#1158 : 质数相关
时间限制:2000ms
单点时限:1000ms
内存限制:256MB
时间限制: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));
}
}