最近突然发现这道题在我的洛谷账号上为40分,打开一看发现是去年刚学dfs还没学dp的时候用dfs写的
然后已经学习了dp的我发现这道题如此简单
先粘贴一下我去年写的dfs代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; int ans=0; int s; int ys(int x){ int sum=0; if(x==1) return 0; for(int i=1;i<x;i++){ if(x%i==0){ sum+=i; } } return sum; } void f(int x,int y,int z){ if(x==0||z==0) { if(y>ans){ ans=y; //cout<<"one turn"<<endl; return ; } return ; } if(x<=z) { //cout<<"one time's change"<<endl; f(x-1,y+ys(x),z-x); } f(x-1,y,z); return ; } int main() { //cout<<ys(3)<<endl; cin>>s; f(s,0,s); cout<<ans; return 0; }
非常直观,这道题就是直接用dfs枚举出所有情况所以T掉6个点也是很正常的
所以直接考虑使用dp
一维dp数组状态:dp[i]表示当s==i时的最大约数之和
用num(i)表示i的所有除了i的约数之和
那么很容易写出状态转移方程:dp[i]=max(dp[i],num(i)),dp[i]=max(dp[i],dp[k]+dp[i-k])(k表示所有从1到i-1的正整数)
初始状态:dp[1]=0
易得AC代码:
#include<bits/stdc++.h> using namespace std; int n; int dp[1010]; int num(int); int main(){ scanf("%d",&n); dp[1]=0; for(int i=2;i<=n;i++){ int tmp=num(i); dp[i]=max(dp[i],tmp); for(int j=1;j<i;j++){ dp[i]=max(dp[i],dp[j]+dp[i-j]); } } printf("%d",dp[n]); return 0; } int num(int x){ int sq=sqrt(x),k=-x; for(int i=1;i<=sq;i++){ if(x%i==0) k+=i,k+=x/i; } if(sq*sq==x) k-=sq; //如果是完全平方数就减1 return k; }
THE END.