试题 算法提高 拿糖果
资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
妈妈给小B买了N块糖!但是她不允许小B直接吃掉。
假设当前有M块糖,小B每次可以拿P块糖,其中P是M的一个不大于根号下M的质因数。这时,妈妈就会在小B拿了P块糖以后再从糖堆里拿走P块糖。然后小B就可以接着拿糖。
现在小B希望知道最多可以拿多少糖。
输入格式
一个整数N
输出格式
最多可以拿多少糖
样例输入
15
样例输出
6
数据规模和约定
N <= 100000
题解
状态的设计:dp[i] 表示有i个糖果能拿到的最大数量
状态的转移:dp[i]=max(dp[i],dp[i-2*a]+a) 假设拿了a个糖果,那么拿的数量就是,已有的a个糖果,加上再去掉妈妈拿走的糖果dp[i-2**a]。因为i一旦确定,dp[i]就已经是确定的数了,所有可以从后往前慢慢推出答案。
埃式筛法:因为本题要求拿走的糖果是糖果总数的素因子,判断一个数是否是另一个数的因子,可以用a%b==0来判断,判断其是否为素数,可以使用埃式筛法,这种方法可以很快速的筛出1e7内的素数,其代码很简洁易懂。
AC代码如下
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100000+1;
int N;
int dp[MAXN];
bool cnt[MAXN]; //0为素数
void getprimes(){ //埃式筛法
for(int i=2;i*i<=MAXN;i++)
if(!cnt[i])
for(int j=i*i;j<MAXN;j=j+i)
cnt[j]=1;
}
int dfs(int N){
if(N<=3) //当糖果总数小于3时,已经不能拿到糖果
return 0;
if(dp[N]) //记忆化搜索
return dp[N];
for(int i=2;i*i<=N;i++) //遍历所有情况,其因子必定小于等于sqrt(N),但是i*i的写法较后者好
if(!cnt[i]&&N%i==0) //i是素数而且i是N的因子
dp[N]=max(dp[N],dfs(N-2*i)+i);
return dp[N];
}
int main(){
memset(dp,0,sizeof(dp));
scanf("%d",&N);
getprimes(); //筛出素数
dfs(N);
printf("%d",dp[N]);
return 0;
}