题目大意:
就是现在给出最多8个数(2 <= ai <= 10^12), 问按照题意建立一个divisor tree, 该tree至少有几个节点
大致思路:
就是一个平常的暴搜问题, 细节和注意的地方写在代码注释里了, 具体看代码吧
代码如下:
Result : Accepted Memory : 4 KB Time : 30 ms
/*
* Author: Gatevin
* Created Time: 2015/3/3 21:06:10
* File Name: Shana.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
/*
* 首先可以发现一个事实, 就是在生成的divisor树中叶子节点一定是质数
* 那么a[i]出现的位置可能是叶子节点当且仅当a[i]是质数
* 另外可以发现所有非根节点和叶子节点的位置一定都是a数组中的数, 这样才能保证节点数最小
* 而且当a[i]最为a[j]的子节点的时候, 相当于a[i]的所有质因数叶子节点替代了a[j]的一部分
* 也就是说, 最终叶子节点的个数取决于与根节点相连的数的divisors, 当然这包括了质数的a[i]
* 所以需要减去a[i]中质数的个数防止重复计数
* 还有一点要考虑的是根节点的问题, 我们刚开始可以虚拟根节点是0, 所有数都被0整除可以连在根节点上
* 但是当根节点上直连接一个点的时候可以去掉根节点, 否则要用一个连在根节点上的所有节点的积来
* 代替根节点的0, 这样对于从大到小的每一个数, 都可以选择接在根节点后或者其倍数后面
* dfs一下寻找最优解即可, 复杂度O(n!)
*/
const int inf = 1e9 + 7;
int n;
lint a[10];
int divisors[10];//表示a[i]的质因子个数
vector <lint> tree;//树上的节点tree[1]是根0
int prime_cnt;//a[i]中的质数个数
/*
* now表示开始考虑a数组中的第i个数(从n到1)
* ai_with_roots表示与根节点相连的a[i]的数量
* divisors_cnt表示和根节点相连的a[i]的divisor[i]的和
* 同时也是叶子节点数(重复计了质数a[i])
*/
int dfs(int now, int ai_with_roots, int divisors_cnt)
{
if(now == 0)
return n + (ai_with_roots > 1) + divisors_cnt - prime_cnt;
int ret = inf;
for(unsigned int i = 0; i < tree.size(); i++)
{
if(tree[i] % a[now] == 0)
{
tree[i] /= a[now];//now接在tree[i]之后tree[i]要剩下一部分供其它可能的接入
tree.push_back(a[now]);
ret = min(ret, dfs(now - 1, ai_with_roots + (i == 0), divisors_cnt + (i == 0 ? divisors[now] : 0)));
tree[i] *= a[now];
tree.pop_back();
}
}
return ret;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%I64d", a + i);
sort(a + 1, a + n + 1);
for(int i = n; i > 0; i--)
{
lint ta = a[i];
for(lint j = 2; j * j <= ta; j++)
{
while(ta % j == 0)
{
ta /= j;
divisors[i]++;
}
}
if(ta > 1) divisors[i]++;
if(divisors[i] == 1) prime_cnt++;
}
tree.push_back(0);
printf("%d\n", dfs(n, 0, 0));
return 0;
}

本文详细介绍了如何通过暴力搜索方法构建一个divisor tree,其中节点数量至少为8个,且每个节点值范围在2至10^12之间。重点阐述了如何通过质因数分解和树结构优化来减少节点数量,最终使用深度优先搜索找到最优解。

493

被折叠的 条评论
为什么被折叠?



