算法提高 拿糖果(動態規劃 + 埃式素數篩法)

試題 算法提高 拿糖果

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