题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3092
题目大意:
给一个数n,求一个划分a1,a2,...ak 使得ans=lcm(a1,a2,..,ak)最大,输出ans%m。
解题思路:
对于一个划分肯定是任意的i,j gcd(ai,aj)=1最优。如果gcd(ai,aj)>1 则最小公倍数肯定小些。
预处理所有的质数,转为话一个完全背包问题。
由于lcm很大,所以取对数处理。
代码:
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<string> #include<cstring> #include<algorithm> #include<vector> #include<map> #include<set> #include<stack> #include<list> #include<queue> #define eps 1e-6 #define INF 0x3fffffff #define PI acos(-1.0) #define ll __int64 #define lson l,m,(rt<<1) #define rson m+1,r,(rt<<1)|1 #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; #define Maxn 3000 //三千内的质数430个 double dp[Maxn+10]; //取对数保证最小公倍数不会溢出 bool tmp[Maxn+10]; int pp[500],ans[Maxn+10]; int n,m,cnt; void init() { cnt=0; memset(tmp,false,sizeof(tmp)); for(int i=2;i<=Maxn;i++) //素数晒选法 { if(!tmp[i]) { pp[++cnt]=i; for(int j=i*2;j<=Maxn;j+=i) tmp[j]=true; } } return ; } void solve() { memset(dp,0,sizeof(dp)); for(int i=0;i<=n;i++) ans[i]=1; for(int i=1;i<=cnt&&pp[i]<=n;i++) { double tt=log10(pp[i]*1.0); for(int j=n;j>=pp[i];j--) //相同质数应做为一个整体考虑 { for(int k=pp[i],num=1;k<=j;k=k*pp[i],num++) if(dp[j-k]+tt*num>dp[j]) { dp[j]=dp[j-k]+tt*num; ans[j]=(ans[j-k]*k)%m; } } } } int main() { init(); //printf("%d\n",cnt); while(~scanf("%d%d",&n,&m)) { solve(); printf("%d\n",ans[n]); } return 0; }