贪心+容斥原理-337E. Divisor Tree

题目链接:

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;
}



你可能感兴趣的:(数学题)