目录
练习题2.1 最基础的“穷竭搜索”
练习题2.2 一直往前!贪心法
练习题2.3 记录结果再利用的“动态规划”
练习题2.4 加工并存储数据的数据结构
练习题2.5 它们其实都是“图”
练习题2.6 数学问题的解题窍门
辗转相除法
POJ 2429 GCD & LCM Inverse
POJ 1930 Dead Fraction
素数
POJ 3126 Prime Path
POJ 3421 X-factor Chains
POJ 3292 Semi-prime H-numbers
快速幂运算
POJ 3641 Pseudoprime numbers
POJ 1995 Raising Modulo Numbers
练习题2.1 最基础的“穷竭搜索”
练习题2.2 一直往前!贪心法
练习题2.3 记录结果再利用的“动态规划”
练习题2.4 加工并存储数据的数据结构
练习题2.5 它们其实都是“图”
练习题2.6 数学问题的解题窍门
辗转相除法
POJ 2429 GCD & LCM Inverse
难题
POJ 1930 Dead Fraction
注意方法,掌握纯循环小数特点然后进行相应转化
素数
POJ 3126 Prime Path
题意:给定两个四位素数a,b,要求把a变换到b。变换的过程要保证每次变换出来的数都是一个四位素数,而且当前这步的变换所得的素数 与前一步得到的素数只能有一个位不同,而且每步得到的素数都不能重复。
注意点:
- 素数筛预处理1000~9999中的素数加快速度。
- bfs不要写残了
本题重点是BFS搜索,注意姿势。对于vis数组,本题的范围较小可以直接用,且复杂度为O(1)。如果用Map来作为这个哈希表,则会产生额外的复杂度。
C++ STL使用哈希表需要#include<hash_map>
,其他用法与map类似。
总体来说,hash_map 查找速度会比map快,而且查找速度基本和数据数据量大小,属于常数级别;而map的查找速度是log(n)级别。并不一定常数就比log(n)小, hash还有hash函数的耗时,明白了吧,如果你考虑效率,特别是在元素达到一定数量级时,考虑考虑hash_map。但若你对内存使用特别严格,希望程序尽可能少消耗内存,那么一定要小心,hash_map可能会让你陷入尴尬,特别是当你的hash_map对象特别多时,你就更无法控制了,而且 hash_map的构造速度较慢。
现在知道如何选择了吗?权衡三个因素: 查找速度, 数据量, 内存使用。
本题部分代码如下:
const int INF = 1<<30;
int t,a,b,prime[10010],vis[10010];
void bfs(int a,int b){
int res=INF;
queue<pair<int,int> >qu;
qu.push(mp(a,0));
CLR(vis);
vis[a]=1;
while(!qu.empty()){
pair<int,int> pr = qu.front();
qu.pop();
int num = pr.first;
if(num==b){
res = min(res,pr.second);
}
int bas = 1;
RREP(k,4,1){
int l = 0,r=9;
if(k==1) l=1;
int pos = num%10;
REP(i,l,r){
if(i!=pos){
int pm = pr.first + (i-pos)*bas;
if(!vis[pm] && prime[pm]){
vis[pm]=1;
qu.push(mp(pm,pr.second+1));
}
}
}
bas *= 10;
num /= 10;
}
}
if(res==INF) printf("Impossible\n");
else printf("%d\n",res);
}
C++ hash_map原理:首先分配一大片内存,形成许多桶。是利用hash函数,对key进行映射到不同区域(桶)进行保存。其插入过程是:
- 得到key
- 通过hash函数得到hash值
- 得到桶号(一般都为hash值对桶数求模)
- 存放key和value在桶内
其取值过程是:
- 得到key
- 通过hash函数得到hash值
- 得到桶号(一般都为hash值对桶数求模)
- 比较桶的内部元素是否与key相等,若都不相等,则没有找到。
- 取出相等的记录的value
hash_map中直接地址用hash函数生成,解决冲突,用比较函数解决。这里可以看出,如果每个桶内部只有一个元素,那么查找的时候只有一次比较。当许多桶内没有值时,许多查询就会更快了(指查不到的时候)。
POJ 3421 X-factor Chains
题意:因子链是将一个数X分解成从1到X的数列,前一个数可以整除后一个数。 求出一个数字\(n(1<=n<=2^{20})\)最大链长和链的个数。
注意点:分解质因数和组合数的简便优雅写法。
本题关键代码如下:
int n;
int res[2000];
int Cnm(int n,int m){
int res = 1;
REP(i,1,m){
res = res * (n-i+1)/i;
}
return res;
}
void solve(int n){ //分解质因数形成链
CLR(res);
int len=0,s=0;
for(int i=2;i*i<=n;i++){
if(n%i==0){
int cnt=0;
while(n%i==0){
cnt++;
n /= i;
}
len += cnt;
res[++s]=cnt;
}
}
if(n!=1){
len ++;
res[++s]=1;
}
printf("%d ",len);
int ans = 1;
REP(i,1,s){ //组合数求值
ans *= Cnm(len,res[i]);
len -= res[i];
}
printf("%d\n",ans);
}
POJ 3292 Semi-prime H-numbers
题意:一个H-number是所有的模四余一的数。如果一个H-number是H-primes当且仅当它的因数只有1和它本身(除1外)。
一个H-number是H-semi-prime当且仅当它只由两个H-primes的乘积表示。H-number剩下其他的数均为H-composite。
给你一个数h,问1到h有多少个H-semi-prime数。(\(1<=h<=10^{6}\))
注意点:如果按照已知素数然后进行乘积组合的方法进行判断,则一定要求出\(10^6\)范围内的全部素数,不能因为1000左右的两个H-prime乘积会超过\(10^6\),就之求解1~1000范围内的”H-prime”。
如果已经求解出范围内的所有”H-primes”,然后需要判断\(10^{6}\)范围内的数字是不是”H-semi-prime”。注意如果相乘后的数字大于\(10^{6}\),需要break掉。
预处理得出所有结果,根据输入输出相应值即可。
本题关键部分代码:
const int maxn=1000010;
int n,flag[maxn],hprime[maxn],ans[maxn];
void solve(){
REP(i,2,maxn){
flag[i] = 1;
}
flag[1]=0;
int s=0;
REP(i,2,maxn){
if(flag[i]){
if(i%4!=1) flag[i]=0;
else{
for(int j=2*i;j<=maxn;j+=i)
flag[j]=0;
hprime[++s]=i;
}
}
}
REP(i,1,s){
REP(j,i,s){
ll res = (ll)hprime[i]*hprime[j];
if(res>maxn) break;
ans[hprime[i]*hprime[j]]=1;
}
}
REP(i,1,maxn) ans[i]+=ans[i-1];
}
还有一种方法是一次循环全部迭代更新结束,写法很优雅,速度更快。注意姿势!!
const int size=1000001;
int H_Number[size+1];
/*筛法打表*/
void Table(void)
{
memset(H_Number,0,sizeof(H_Number)); //H_Number[i]=0 表示 i为H-prime
for(int i=5;i<=size;i+=4)
{
for(int j=5;j<=size;j+=4)
{
int multiply=i*j;
if(multiply>size)
break;
if(H_Number[i]==0 && H_Number[j]==0) //i与j均为H-prime
H_Number[multiply]=1; //multiply为H-semi-primes
else
H_Number[multiply]=-1; //multiply为H-composite
}
}
int Pcount=0; //H-prime计数器
for(int k=1;k<=size;k++)
{
if(H_Number[k]==1)
Pcount++;
H_Number[k]=Pcount; //从1到k有Pcount个H-semi-primes
}
return;
}
快速幂运算
POJ 3641 Pseudoprime numbers
题意:输入两个数p和a,两个数如果p是素数输出no;如果p不是素数,判断\(a^{p}%p==a\)是否成立,如果成立输出yes,否则输出no。
水题。
POJ 1995 Raising Modulo Numbers
题意:快速幂求和取模。
注意点:注意取模的时候别出错。取模公式ans=(ans+pow_mod(a,b,mod))%mod
。
本题关键部分代码:
int T,M,H;
ll pow_mod(ll a, ll b, ll m){
ll res = 1;
while(b){
if(b&1) res = (res*a)%m;
a = (a*a)%m;
b>>=1;
}
return res;
}
int main(){
_s(T);
while(T--){
_s(M);
_s(H);
ll ans = 0;
REP(i,1,H){
int a,b;
_s(a);_s(b);
ans = (ans+pow_mod(a,b,M))%M;
}
_pl(ans);_pn;
}
}