题目链接:https://vjudge.net/problem/LightOJ-1356
题目大意:
T个 test case,每个 test case 给出一个 N 个数的集合。请找出一个最大的子集,使得子集中的任何一个数除以子集中的任意的另一个数所得到的数不是质数。
解题思路:
先用素数筛找出 1 到 500000 的所有质数。
在输入一个集合的时候,我们顺便记录下输入的这个数在输入数组中的位置,找出它的所有质因数,记录下质因数的总个数,用一个vector记录下所有 “不同的” 质因数。
遍历输入数组中的每一个数,对于每个数,遍历其所有 “不同的” 质因数,如果找到这样的一个质因数:该数除以这个质因数能得到输入数组中的另一个数,那么将这两个数连边。
将由上述的连边操作得到的图中的点分成两类:质因数总个数为奇数的点和质因数总个数为偶数的点(这个划分有点隐晦,我多说两句:其实,对于连边的两个数,二者各自的所有质因数其实就只有一个质数的差别,其中一个数的所有质因数中有这个质数,但另一个数没有,其他的质因数都相同;所以,他们质因数的总个数相差 1,故其中一个为奇数,一个为偶数),这样一来,这个图就变成一个二分图了。而答案其实就是求这个二分图的最大独立集。
另:用匈牙利算法者,T!用 Hopcroft-Carp者方有可能AC。
AC代码:
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <vector> 5 #include <queue> 6 7 using namespace std; 8 const int inf=0x3f3f3f3f; 9 10 bool prime[500003]; 11 int have[500003],num[40003]; 12 int prims[300003]; 13 14 vector<int> G[40003]; 15 int uN; 16 int l[40003]; 17 int Mx[40003],My[40003]; 18 int dx[40003],dy[40003]; 19 int dis; 20 bool used[40003]; 21 bool SearchP(){ 22 queue<int>Q; 23 dis=inf; 24 memset(dx,-1,sizeof(dx)); 25 memset(dy,-1,sizeof(dy)); 26 for(int i=0;i<uN;i++){ 27 if(Mx[l[i]]==-1){ 28 Q.push(l[i]); 29 dx[l[i]]=0; 30 } 31 } 32 while(!Q.empty()){ 33 int u=Q.front(); 34 Q.pop(); 35 if(dx[u]>dis) break; 36 int sz=G[u].size(); 37 for(int i=0;i<sz;i++){ 38 int v=G[u][i]; 39 if(dy[v]==-1){ 40 dy[v]=dx[u]+1; 41 if(My[v]==-1) dis=dy[v]; 42 else{ 43 dx[My[v]]=dy[v]+1; 44 Q.push(My[v]); 45 } 46 } 47 } 48 } 49 return dis!=inf; 50 } 51 bool DFS(int u){ 52 int sz=G[u].size(); 53 for(int i=0;i<sz;i++){ 54 int v=G[u][i]; 55 if(!used[v]&&dy[v]==dx[u]+1){ 56 used[v]=true; 57 if(My[v]!=-1&&dy[v]==dis) continue; 58 if(My[v]==-1||DFS(My[v])){ 59 My[v]=u; 60 Mx[u]=v; 61 return true; 62 } 63 } 64 } 65 return false; 66 } 67 int MaxMatch(){ 68 int res=0; 69 memset(Mx,-1,sizeof(Mx)); 70 memset(My,-1,sizeof(My)); 71 while(SearchP()){ 72 memset(used,false,sizeof(used)); 73 for(int i=0;i<uN;i++){ 74 if(Mx[l[i]]==-1&&DFS(l[i])) res++; 75 } 76 } 77 return res; 78 } 79 void init(){ 80 memset(prime,true,sizeof(prime)); 81 prime[0]=prime[1]=false; 82 int cnt=0; 83 for(int i=2;i<=500000;i++){ 84 if(prime[i]){ 85 prims[cnt++]=i; 86 for(int j=2*i;j<=500000;j+=i) 87 prime[j]=false; 88 } 89 } 90 } 91 int zhis[40003]; 92 vector<int> zhiyinshu[40003]; 93 int main(){ 94 init(); 95 int T,N; 96 scanf("%d",&T); 97 for(int t=1;t<=T;t++){ 98 scanf("%d",&N); 99 memset(have,0,sizeof(have)); 100 memset(zhis,0,sizeof(zhis)); 101 for(int i=1;i<=N;i++){ 102 G[i].clear(); 103 zhiyinshu[i].clear(); 104 scanf("%d",&num[i]); 105 have[num[i]]=i; 106 int tmp=num[i]; 107 for(int j=0;;j++){ 108 if(prime[tmp]){ 109 zhiyinshu[i].push_back(tmp); 110 zhis[i]++; 111 break; 112 } 113 if(tmp%prims[j]==0){ 114 tmp/=prims[j]; 115 zhiyinshu[i].push_back(prims[j]); 116 zhis[i]++; 117 while(tmp%prims[j]==0){ 118 tmp/=prims[j]; 119 zhis[i]++; 120 } 121 } 122 if(tmp<prims[j]) break; 123 } 124 } 125 for(int i=1;i<=N;i++){ 126 for(int j=0;j<zhiyinshu[i].size();j++){ 127 if(have[num[i]/zhiyinshu[i][j]]){ 128 int u=have[num[i]/zhiyinshu[i][j]]; 129 G[i].push_back(u); 130 G[u].push_back(i); 131 } 132 } 133 } 134 135 uN=0; 136 for(int i=1;i<=N;i++){ 137 if(zhis[i]%2==1){ 138 l[uN++]=i; 139 } 140 } 141 printf("Case %d: %d ",t,N-MaxMatch()); 142 } 143 return 0; 144 }