算法提高 拿糖果(动态规划 + 埃式素数筛法)

试题 算法提高 拿糖果

资源限制
时间限制: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章