試題 算法提高 拿糖果
資源限制
時間限制: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;
}