http://hihocoder.com/problemset/problem/1158
描述
两个数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
二分图最大独立集(点数-最大匹配数)。这个题集合的划分很巧妙,一个数的素因子要么有奇数个,要么有偶数个,可以以此为依据划分两个集合。因为两个数要“质数相关”,必然素因子个数一奇一偶,只有这样才有可能连边。最大的质数无关集合,正好对应这个二分图的最大独立集,匈牙利算法跑一下就好了。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
#define ll long long
int n;
int s[maxn];
int s1[maxn],s2[maxn];
int size1,size2;
bool flag[502000];
int primes[41540];
//图相关
int tote;
int head[maxn];
int to[maxn];
int pre[maxn];
void addedge(int u,int v){
to[tote]=v;
pre[tote]=head[u];
head[u]=tote++;
//to[tote]=u;
//pre[tote]=head[v];
//head[v]=tote++;
}
int match[maxn];
int used[maxn];
bool dfs(int u){
for(int i=head[u];~i;i=pre[i]){
int v=to[i];
if(used[v])continue;
used[v]=1;
if(match[v]==-1||dfs(match[v])){
match[v]=u;
return 1;
}
}
return 0;
}
//
//分为2部
void div(){
for(int i=1;i<=n;i++){
int tmp=s[i];
int cnt=0;
for(int j=0;primes[j]<=tmp;j++){
while(tmp%primes[j]==0){
cnt++;
tmp/=primes[j];
}
}
if(cnt&1){
s1[size1++]=s[i];
}else{
s2[size2++]=s[i];
}
}
}
void init(){
size1=size2=0;
tote=0;
memset(head,-1,sizeof(head));
memset(match,-1,sizeof(match));
}
int main(){
//筛1~500000素数
for(int i=2;i<=708;i++){
for(int j=i+i;j<501264;j+=i){
flag[j]=1;
}
}
int cnt=0;
for(int i=2;i<=500000;i++){
if(!flag[i])primes[cnt++]=i;
}
int t;
cin>>t;
int cas=0;
while(t--){
init();
cas++;
cin>>n;
for(int i=1;i<=n;i++){
cin>>s[i];
}
div();
for(int i=0;i<size1;i++){
for(int j=0;j<size2;j++){
if(s1[i]%s2[j]==0&&!flag[s1[i]/s2[j]]){
addedge(i,j);
}else if(s2[j]%s1[i]==0&&!flag[s2[j]/s1[i]]){
addedge(i,j);
}
}
}
//匈牙利算法
int maxflow=0;
for(int i=0;i<size1;i++){
memset(used,0,sizeof(used));
if(dfs(i))maxflow++;
}
printf("Case #%d: %d\n",cas,n-maxflow);
}
return 0;
}