-
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
描述
两个数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
2.解题思路:本题利用最大流解决。首先可以把所有质数相关的数找出来,这里用ss数组来标记它们。接下来是建图过程。如果数字a[i]是质数相关的,那么把i和源点s之间连接一条边。否则,把它和汇点t之间连接一条边。接下来,如果发现数字a[i]=a[j]*p,或者a[j]=a[i]*p,那么将i和j之间连接一条边。所有边的容量均为1.接下来对这个图求解最大流。利用最小割最大流定理可知,n-maxflow()即为包含质数无关点数最多的子集。
3.代码:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<functional>
using namespace std;
int ss[510000], T, a[1100];
bool prime[510000];
#define INF 1000000000
#define min(x, y) ((x) < (y) ? (x) : (y))
int n, m, i, q, h, mid, len, s, t, till[3100000], go[3100000], Next[3100000], f[3100000], D[3100000], n1[3100000];
bool cc[3100000];
void add(int x, int y, int z) {
Next[++len] = till[x];
till[x] = len;
go[len] = y;
f[len] = z;
}
void Ad(int x, int y, int z) {
add(x, y, z);
add(y, x, 0);
}
bool bfs() {
int q, h, i;
memset(D, 0, sizeof D);
memset(cc, 1, sizeof cc);
for (D[n1[q = h = 1] = s] = 1; q <= h; q++)
for (i = till[n1[q]]; i; i = Next[i])
if (f[i] && !D[go[i]]) D[n1[++h] = go[i]] = D[n1[q]] + 1;
return D[t];
}
int dfs(int k, int mi) {
if (k == t) return mi;
int i, tmp, sum = 0;
for (i = till[k]; i && mi; i = Next[i])
if (f[i] && D[go[i]] == D[k] + 1 && cc[go[i]]) {
tmp = dfs(go[i], min(mi, f[i]));
sum += tmp;
mi -= tmp;
f[i] -= tmp;
f[i ^ 1] += tmp;
}
if (!sum) cc[k] = false;
return sum;
}
int maxFlow() {
int sum = 0;
while (bfs()) sum += dfs(s, INF);
return sum;
}
int main()
{
freopen("t.txt", "r", stdin);
ss[1] = 0;
for (int i = 2; i <= 500000; i++)//筛素数
prime[i] = true;
for (int i = 2; i <= 500000; i++) {
for (int j = i + i; j <= 500000; j += i) prime[j] = false;
}
for (int i = 1; i <= 500000; i++)
for (int j = 1; j <= 500000 / i; j++)
if (prime[j]) ss[i * j] = 1 - ss[i];//把所有质数相关的数标记为1
scanf("%d", &T);
for (int mm = 1; mm <= T; mm++) {
printf("Case #%d: ", mm);
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
len = 1;
s = 0;//源点
t = n + 1;//汇点
for (int i = 0; i <= n + 1; i++)
till[i] = 0;
for (int i = 1; i <= n; i++)
if (ss[a[i]]) Ad(0, i, 1);//如果a[i]是质数相关的数,源点和i连接一条边
else Ad(i, n + 1, 1);//否则,i和汇点连接
for (int i = 1; i <= n; i++)
if (ss[a[i]])
for (int j = 1; j <= n; j++)
if (!ss[a[j]]) {
if (a[i] > a[j]) {
if (a[i] % a[j] == 0 && prime[a[i] / a[j]]) Ad(i, j, 1);//如果a[i]是质数相关且那个质数为a[i]/a[j],为i和j之间连接一条边
}
else {
if (a[j] % a[i] == 0 && prime[a[j] / a[i]]) Ad(i, j, 1);
}
}
printf("%d\n", n - maxFlow());//用Dinic算法求最大流,利用最小割最大流定理,剩下的点就是最大的子集
}
}