两个数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
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算法求最大流,利用最小割最大流定理,剩下的点就是最大的子集 } }
原文地址:http://blog.csdn.net/u014800748/article/details/45287773