题目链接:
http://codeforces.com/problemset/problem/337/E
题目大意:
给n个数(n<=8),每个节点有个数,求节点数最少的包含这n个数的树,使得该树的叶子节点为质数,且每个非叶子节点为所有儿子的乘积。
解题思路:
首先统计该n个数中非质数的所有的质因子的个数的和。相当于叶子节点,不过当某个数为另外一个数的父亲时,统计多了,所以后面要减去。
然后从大往后枚举每个数A做为一个树根,依次找到含质因数个数最多的那个且能整除A的数B做为A的儿子,此时B就可以做为A的儿子,可以省去A的质因数个数个节点。再把第一个儿子排除在外再找第二个儿子,,,如此类推知道不能找到一个节点做为他的儿子,此时保证了以它做为父亲时能最大减少节点的数量,因为无论怎样他都有占有这么多的因子数量。
最后再统计所有的树根,如果树根超过1的话,再增加一个树根。
代码:
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<map> #include<set> #include<stack> #include<list> #include<queue> #define eps 1e-6 #define INF 0x1f1f1f1f #define PI acos(-1.0) #define ll __int64 #define lson l,m,(rt<<1) #define rson m+1,r,(rt<<1)|1 #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); #define Maxn 10 ll a[Maxn]; int b[Maxn]; bool vis[Maxn]; int main() { int n; while(~scanf("%d",&n)) { for(int i=1;i<=n;i++) scanf("%I64d",&a[i]); sort(a+1,a+n+1); memset(b,0,sizeof(b)); ll ans=0; for(int i=1;i<=n;i++) { ll temp=a[i]; for(ll j=2;j*j<=temp;j++) { while(temp%j==0) b[i]++,temp/=j; } if(temp!=1) b[i]++; if(b[i]!=1) ans+=b[i]; } //求出a的质因数个数 memset(vis,false,sizeof(vis)); //容斥原理+贪心思想,先把公共质因子最多的安顿好。 int num=0,la,p; //printf(":%I64d \n",ans); for(int i=n;i>=1;i--) { if(!vis[i]) //c[i]=0表示没有作为前面的数的因子出现 num++; while(true) { la=0; for(int j=i-1;j>=1;j--) { if(!vis[j]&&a[i]%a[j]==0&&b[j]>la) { la=b[j]; p=j; } } if(la) //说明找到了一个,继续找 { //printf("i:%d a[i]:%I64d b:%I64d la:%I64d\n",i,a[i],b[p]); //system("pause"); vis[p]=true; a[i]/=a[p]; ans=ans-la+1; //公共因子算了两次,减去,并把该数加进去 } else //没找到 break; } } ans+=num; //这么多的树根 if(num>1) //需要另外再找一个树根 ans++; printf("%I64d\n",ans); } return 0; }