描述
两个数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
思路 筛法求范围内所有质数。枚举集合内任意两个数,求它俩是否质数相关。是则加一条无向边,建图。这个图是一个无向的森林,这一点可以简单证明下:
若a1和a2有边连接到b,则有a1*p1 = a2 * p2 = b。不妨设a2 > a1。
假设a1和a2间还另有一条通路.那么有两种情况,一种存在c = a1*p3 = a2*p4。c/b 得到 p3/p1 = p4/p2 两个不同质数之比不会相等,因此矛盾。
另一种情况,a1 是 a2 的因数则有a1 * k = a2 ,那么有a1*k*p2 = a1*p1 得到k*p2 = p1 ,这与p1是质数矛盾。
因此a1 和 a2间无回。
因此可以进行树dp。因为我们要删去一些点,让森林中无边,因此父节点和它下层间最多有一层被保留,因此设计状态dp(i,j)表示i这棵子树,i节点被保留或不保留时的最多节点数。
//#include <bits/stdc++.h> //国外OJ支持
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdlib>
#include <map>
#include <assert.h>
using namespace std;
#define I64_MAX 9223372036854775807
typedef long long ll;
const double pi=acos (-1.0);
const double eps=1e-8 ;
//const ll INF=(I64_MAX)/2;
//#pragma comment(linker, "/STACK:102400000,102400000")
const int inf=0x3f3f3f3f ;
#define maxx(a) memset(a, 0x3f, sizeof(a))
#define zero(a) memset(a, 0, sizeof(a))
#define FILL(a,b) memset(a, b, sizeof(a))
#define REP(i,a,b) for(i=a;i<b;i++)
#define rep(i,n) REP(i,0,n)
#define srep(i,n) for(i = 1;i <= n;i ++)
#define snuke(c,itr) for( __typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
#define MP make_pair
#define fi first
#define se second
typedef pair <int, int> PII;
typedef pair <ll, ll> PX;
typedef pair<int,ll> PIL;
#define MAX 500000
const int maxn = 1002;
int n,m;
bool is_prime[MAX+1];
int s[maxn];
int g[maxn];
int dp[maxn][2];
struct Edge{
int u,v,next;
}edge[maxn<<1];
void addEdge(int u,int v){
edge[m<<1].u = u;
edge[m<<1].v = v;
edge[m<<1].next = g[u];
g[u] = (m<<1);
edge[(m<<1)|1].v = u;
edge[(m<<1)|1].u = v;
edge[(m<<1)|1].next = g[v];
g[v] = (m<<1)|1;
}
void sieve(){
int n = MAX;
for(int i=0;i<=n;i++){
is_prime[i] = 1;
}
is_prime[0] = is_prime[1] = 0;
for(int i=2;i<=n;i++){
if(is_prime[i]==1){
for(int j=2*i;j<=n;j+=i)
is_prime[j] = false;
}
}
}
void dfs(int x){
if(dp[x][0] != -1)
return;
dp[x][0] = 0;
dp[x][1] = 1;
for(int e=g[x];e!=-1;e=edge[e].next){
int v = edge[e].v;
if(dp[v][0] != -1)
continue;
dfs(v);
dp[x][0] += max(dp[v][0],dp[v][1]);
dp[x][1] += dp[v][0];
}
}
int main ()
{
// freopen("E:\\input.txt" ,"r", stdin);
//freopen ("E:\\out.txt","w",stdout);
int i,j;
int T,t;
memset(is_prime,0,sizeof(is_prime));
sieve();
scanf("%d",&T);
for(t=1;t<=T;t++)
{
m = 0;
scanf("%d",&n);
memset(g,-1,sizeof(g));
memset(dp,-1,sizeof(dp));
for(i=0;i<n;i++){
scanf("%d",&s[i]);
}
sort(s,s+n);
for(i=0;i<n;i++){
for(j=i+1;j<n;j++){
if(s[j] % s[i] != 0)
continue;
int tmp = s[j] / s[i];
if(is_prime[tmp] == 1)
{
addEdge(i,j);
m++;
}
}
}
int now = n;
dp[n][0] = 0;
for(i=0;i<n;i++){
if(dp[i][0] == -1)
{
dfs(i);
dp[n][0] += max(dp[i][0],dp[i][1]);
}
}
printf("Case #%d: %d\n",t,max(dp[n][0],dp[n][1]));
}
return 0;
}