hdu - 4345 - Memory Control - 数论
来源:互联网 发布:pc安装mac os x10.10 编辑:程序博客网 时间:2024/04/29 03:29
http://acm.hdu.edu.cn/showproblem.php?pid=4345
问题即是求相加小于等于n的质数的最小公倍数有几个。推荐网站:http://oeis.org/,只需将前面几个数字的答案输入,即可得出序列,打表即过(每想到这里,我就想笑)。
解题报告说用记忆化搜索,没想明白,估计就是跟dp差不多,用前面的去推后面的。
今晨参悟http://blog.csdn.net/cyberzhg/article/details/7840340代码,有感于心,记录一下思路。
首先,考虑n的答案是不是可以由n - 1得来。答案是肯定的。这么设计:
用ans[i]表示状态i的答案数;
用a[i][j]记录表示将相加等于i的质数的最小公倍数的方案数,且最小质数的序号为j;
状态转移方程:
因为素数px跟谁都互质,所以呢,如下
a[i][j] += a[i – p1][大于j];(因为j表示的是最小的序号,所以肯定需要大于序号j的素数去组成i – p1)
a[i][j] += a[i – p1^2][大于j];
a[i][j] += a[i – p1^3][大于j];
……
i - p1 ^ x >= 0
a[i][j] += a[i – p2][大于j];
a[i][j] += a[i –p2 ^ 2][大于j];
a[i][j] += a[i – p2 ^ 3][大于j];
…… ……
pn ^ x < i就行
当pn^x == i时,只有一个最大公倍数方案并且前面不曾出现过,a[i][j] ++;
这个其实就是转换了求解的方法
因为包含最小素因数为prime[j]的部分已经被考虑在tmp里面了,剩下的i-tmp里面不可以再有prime[j]作为素因数,不然的话情况就是接下来tmp*prime[j]那种了。
其实就是分类讨论 ,用数学的思想去想!
当我们得出所有的a[i][j]之后,对于每个i将值累加到a[i][0]里面。此时,a[i][0]存的就是求相加等于n的质数的最小公倍数有几个。
然而,问题是求相加小于等于n的质数的最小公倍数有几个,此时就要将ans[i]和ans[i – 1]联系起来了。
ans[i]= ans[i - 1] + a[i][0];
为什么这样子,会不会有重复呢?
相加为i的各种质数和相加为i – 1的各种质数的最小公倍数肯定不相同。
所以,记忆化搜索完成。其实,就是递推,由i - 1推出i。
/*Pro: 0Sol:date:*/#include <cstdio>#include <iostream>#include <cstring>#include <cmath>#include <set>#include <algorithm>using namespace std;#define maxn 1001long long ans[maxn],a[maxn][maxn];int prime[maxn],primeNumber,flag[maxn];void getprime(){ memset(flag,true,sizeof(flag)); for(int i = 2; i < maxn; i ++){ for(int j = i + i; j < maxn; j += i) flag[j] = false; } primeNumber = 0; for(int i = 2; i < maxn; i ++){ if(flag[i]) prime[primeNumber ++] = i; }}void getans(){ memset(a, 0, sizeof(a)); for(int i = 2; i < maxn; i ++){//对于每一个可以分解质因数的i分解 for(int j = 0; j < primeNumber; j ++){ if(prime[j] > i) break; long long tmp = 1; while(tmp <= i){ tmp *= prime[j];//prime[j]可以取很多个,针对每一个i if(i - tmp > 0) for(int k = j + 1; k < primeNumber; ++ k) a[i][j] += a[i - tmp][k];//这个关系式极其重要 else if(i - tmp == 0){//等于0,那么只有一种取法,需要加上去 a[i][j] ++; break; } else break; } } } ans[0] = 0; ans[1] = 1; for(int i = 2; i < maxn; i ++) for(int j = 1; j < primeNumber; j ++) a[i][0] += a[i][j]; for(int i = 2; i < maxn; i ++) ans[i] = ans[i - 1] + a[i][0];}void init(){ getprime(); getans();}int n;int main(){ init(); while(scanf("%d",&n) != EOF){ printf("%I64d\n",ans[n]); } return 0;}
再贴一个dfs的代码:
#include <iostream> #include <cstdio> #include <cmath> #include <string> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; const int inf = 0x3f3f3f3f; const int N = 1100; const double esp = 1e-6; int pri[N], vis[N]; __int64 ans[N][N]; int cnt; void prime() { int i, j; cnt = 0; for(i=2; i<N; i++) if(!vis[i]) { pri[cnt++] = i; for(j=i+i; j<N; j+=i) vis[j] = 1; } } __int64 dp(int m, int n, int i) { if(ans[n][i]!=-1) return ans[n][i]; if(i>m) return ans[n][i] = 1; ans[n][i] = dp(m, n, i+1);//首先是第i个质数不存在的情况 int k = pri[i]; while(k<=n) { ans[n][i] += dp(m, n-k, i+1);//然后枚举他的幂次数存在 k *= pri[i]; } return ans[n][i];} int main() { int i, n, m; prime(); while(~scanf("%d", &n)) { for(i=0; pri[i]<=n; i++); m = i-1; memset(ans, -1, sizeof(ans)); printf("%I64d\n", dp(m, n, 0)); } return 0; }
好神啊!
- hdu - 4345 - Memory Control - 数论
- hdu 2871 Memory Control
- hdu 2871 Memory Control
- hdu 2871 Memory Control
- HDU 2871 Memory Control
- HDU-2871-Memory Control
- hdu 2871 Memory Control
- HDU 2871 Memory Control
- 【23.68%】【hdu 2871】Memory Control
- hdu 2871 Memory Control 线段树
- 线段树 HDU 2871 memory control
- hdu 2871 vector + 线段树 Memory Control
- 线段树 hdu 2871 memory control
- hdu 2871 Memory Control (线段树&vector)
- hdu 2871 Memory Control(线段树)
- HDU 2871 Memory Control 线段树
- hdu 2871 Memory Control 线段树
- hdu 2871 Memory Control(线段树)
- MySQL常用命令集合
- 最远有多远
- PopupWindow的使用
- 关于git rebase 后 push rejected
- 在SQLServer中查询时区分大小写
- hdu - 4345 - Memory Control - 数论
- vnc viewer控制andriod的快捷键
- 《信心抽取研究概述》读书摘要
- Windows C++ 调用带参数exe
- html文件中包含其他文件的方法大全
- 醉翁之意不在酒:专家揭露苹果控告三星的真正动机
- java:ajax实现无跳转刷新的三种方法
- 给oracle设置自动增长列
- 【转】 php加速 PHP APC 浅析