1103. Integer Factorization (30)[递归遍历+剪枝]

1. 原题: https://www.patest.cn/contests/pat-a-practise/1103

2. 思路:

题意:
判断一个数能否分解成N个因子的p次方的和。
若是,求出最大的N个因子和。
有多个相同最大和,输出较大的那个序列。比如 1) 1, 2, 3, 5
2) 1, 2, 4, 4
则输出第二种。
递归剪枝题。


思路:
题意并不算多难,但是如何转换成合适的算法呢。
首先会想到暴力破解的方式,毕竟x<=400, 那么因子最大是20.
所以不妨从1~20一个个试得了。这样的思路是对的。
我们可以优化。毕竟是N个因子,超出N个的话就不需要继续判断了。
遍历的过程中,我们从最小值1开始测试,同时记录因子和,若遇到更大的或者相等的,都更新。
这样也满足题目中的多个相同和的情况。


思路整理下,就是递归+剪枝优化的算法。
即当前元素值x,把因子i作为它的一个,那么新的x = x - i^p, 把因子i加入容器factor中.
接着对新的x递归遍历,这时的遍历因子从i开始,而不是从1开始了(因为前面的已经递归处理了)。
递归完成后输出结果就好了。
已AC。

3. 源码:

#include<iostream>
#include<vector>
#include<algorithm>//使用sort排序函数,pow指数函数
#include<functional>//使用内置降序比较greater仿函数
using namespace std;

int x, N, p;//分别为给定的正数, 因子个数及指数
int maxsum = -1, cur_sum = 0;//分别为因子和的最大值,当前因子和
vector<int> factor, result;//分别为因子和最终所求的因子的数组容器
void dfs(int x, int cnt);//递归遍历
void print();//输出结果

int main(void)
{
	//freopen("in.txt", "r", stdin);
	cin >> x >> N >> p;
	factor.resize(N);
	dfs(x, 0);//递归遍历因子,参数为元素值,因子的下标顺序。N个因子,下标从0~N-1

	if (maxsum == -1)//说明未找到
		cout << "Impossible\n";
	else
		print();

	return 0;
}

void dfs(int x, int cnt)//递归遍历因子,参数为元素值,因子的下标顺序。N个因子,下标从0~N-1
{
	if (cnt == N && x != 0)//剪枝优化,已经有N个因子,但和不等于给定的值
		return;
	if (x < 0)//剪枝优化,超出了给定值。直接返回
		return;

	if (x == 0 && cnt == N)//可分解成N个因子,更新
	{
		cur_sum = 0;//因子和
		for (int i = 0; i < N; i++)
			cur_sum += factor[i];

		if (cur_sum >= maxsum)//更新最大和,相等也更新,这样才满足最大的因子序列
		{
			maxsum = cur_sum;
			result = factor;//因子赋值给结果容器
		}
		return;
	}

	int start = cnt > 0 ? factor[cnt - 1] : 1;//因子起点,第一个因子递归时,因子值从1开始。否则是上一个因子值
	int end = (int)sqrt(double(x));//因子终点
	for (int fac = start; fac <= end; fac++)//因子值从小到大进行递归遍历
	{
		factor[cnt] = fac;//把新的因子压入容器
		dfs(x - (int)pow(fac, p), cnt + 1);
	}

	return;
}

void print()//输出
{
	sort(result.begin(), result.end(), greater<int>());
	printf("%d = ", x);

	for (int i = 0; i < result.size(); i++)
	{
		if (i != 0)
			printf(" + ");
		printf("%d^%d", result[i], p);
	}
	printf("\n");
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值