蓝桥杯:拿糖果 记忆搜索解法
问题描述
妈妈给小B买了N块糖!但是她不允许小B直接吃掉。
假设当前有M块糖,小B每次可以拿P块糖,其中P是M的一个不大于根号下M的质因数。这时,妈妈就会在小B拿了P块糖以后再从糖堆里拿走P块糖。然后小B就可以接着拿糖。
现在小B希望知道最多可以拿多少糖。
输入格式
一个整数N
输出格式
最多可以拿多少糖
样例输入
15
样例输出
6
数据规模和约定
N <= 100000
思路
这题DP的点比较明显:
假设当前有M块糖,小B每次可以拿P块糖,其中P是M的一个不大于根号下M的质因数。这时,妈妈就会在小B拿了P块糖以后再从糖堆里拿走P块糖。然后小B就可以接着拿糖。
状态定义与递推的推导:
假设现在从 j 块的堆中拿了 i 块,那么妈妈🐎也会拿 i 块,于是糖堆还剩下 j - 2 * i 块糖,在 j 块的糖堆中拿 i 块的最优解,就转变成 【在 j - 2 * i 的糖堆中的最优解 + i 】了
单次递归推导
- 定义 dfs(x) 为求取在数量为 x 的糖堆中拿糖的最优解
- 假设现在糖堆有 x 个糖,穷举 x 的所有质因数 z
- 逐一计算 【dfs(x-2*z) + z】的值,取最大的,作为 dfs(x) 的解
递归边界条件
- 如果是数量为负数的糖堆,拿不了,所以 return 0
- 如果糖堆数量是 1,2,3这样的整数,他们没有质因数,拿不了,return 0
代码
#include <iostream>
#include <math.h>
using namespace std;
#define maxN 100009
int n;
int mem[maxN+1]; // 保存计算结果
// 判断素数
int is_prime(int x)
{
if(x == 1)
{
return 0;
}
if(x == 2)
{
return 1;
}
for(int i=2; i<(int)sqrt(x); i++)
{
if(x%i == 0)
{
return 0;
}
}
return 1;
}
// x是y的质因数吗?
int is(int x, int y)
{
if(y%x==0 && x<=(int)sqrt(y))
{
if(is_prime(x))
{
return 1;
}
}
return 0;
}
// 记忆搜索
int dfs(int x)
{
if(x<=3)
{
return 0;
}
// 穷举x的所有质因数,即穷举所有拿法
int max = 0;
for(int i=2; i<=(int)sqrt(x); i++)
{
if(is(i, x))
{
int res;
if(mem[x-2*i] != -1)
{
res = mem[x-2*i];
}
else
{
res = dfs(x-2*i) + i;
}
if(res > max)
{
max = res;
}
}
}
mem[x] = max;
return max;
}
int main()
{
cin>>n;
for(int j=0; j<=maxN; j++)
{
mem[j] = -1;
}
cout<<dfs(n)<<endl;
return 0;
}
写了一个用数组DP的,没用递归的,但是后台运行错误,本地不会,等找到问题再放出来。。。。
即使是递归,时间也还算充分