算法竞赛进阶指南0x02 递推和递归
枚举总结:指数型枚举的复杂度是(2^n),一般n是十几个,组合型枚举,略低于(2^n),他还和m有关,一般最多能枚举到20左右吧
排列型枚举的复杂度更高,是O(n!)的,所以一般也就撑死了10左右
指数型枚举
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
void dfs(int u, int state){
if(u == n + 1){
for(int i = 0; i < n; i++) if(state >> i & 1) printf("%d ", i+1);
puts("");
return ;
}
dfs(u + 1, state);
dfs(u + 1, state + (1 << (u-1)));
}
int main(){
scanf("%d", &n);
dfs(1, 0);
return 0;
}
组合类型的枚举
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, m;
void dfs(int u, int cnt, int state){
if(cnt == m + 1){
for(int i = 1; i <= n; i++) if(state >> i & 1) printf("%d ", i );
puts("");
return;
}
if(u == n + 1) return ;
dfs(u + 1, cnt + 1, state + (1 << u));
dfs(u + 1, cnt, state);
}
int main(){
scanf("%d %d", &n, &m);
dfs(1, 1, 0);
return 0;
}
排列类型的枚举
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 11;
int n;
int p[N];
int st[N];
void dfs(int u){
if(u == n + 1){
for(int i = 1; i <= n; i ++ ) printf("%d ", p[i]);
puts("");
}
for(int i = 1; i <= n; i++){
if(st[i]) continue;
st[i] = 1;
p[u] = i;
dfs(u + 1);
st[i] = 0;
}
}
int main(){
scanf("%d", &n); dfs(1);
return 0;
}
约数之和
一些注意事项:
1. 等比求和可以使用快速幂优化
2. 注意求逆的时候x%mod==0的时候,一般mod是质数,所以不需要判断mod%x==0,存在逆的充要条件是(x, mod) = 1
3. 注意判断边界 a=0的时候
4. 出现减法的时候,注意最后的答案取成最小正整数
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 9901;
LL qmi(LL a, LL b, LL mod){
LL res = 1 % mod;
while(b){
if(b&1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int main(){
int a, b; scanf("%d %d", &a, &b);
unordered_map<int, int> h;
if(a == 0 ){
cout << 0 << endl;
return 0;
}
for(int i = 2; i <= a / i; i ++){
if(a % i == 0){
h[i] = 0;
while(a % i == 0) h[i] ++, a /= i;
}
}
if(a > 1) h[a] = 1;
int res = 1;
for(auto [x, y]: h){
h[x] *= b;
if((x - 1) % mod == 0) res = (LL)res * (h[x] + 1) % mod;
else res = (LL)res * (qmi(x, h[x] + 1, mod) - 1) % mod * qmi(x - 1, mod - 2 , mod) % mod;
}
cout << (res % mod + mod) % mod << endl;
return 0;
}
分形之城
注意事项:-45的斜线对称写的不是很好,多练下, 平移量多少别写错了,四舍五入直接.0f就可以,别直接转换成int
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int N;
LL qmi(LL a, LL b){
LL res = 1;
while(b){
if(b&1) res = res * a;
a = a * a ;
b >>= 1;
}
return res;
}
void dfs(LL n, LL a, LL &x, LL &y){
if(n == 1){
if(a == 1) x = 1, y = 1;
else if(a == 2) x = 1, y =2;
else if(a == 3) x = 2, y = 2;
else x = 2, y = 1;
return ;
}
LL t = qmi(4, n - 1);
if(a <= t){
dfs(n - 1, a, x, y);
swap(x, y);
}
else if(a <= 2*t){
dfs(n - 1, a - t, x, y);
y += qmi(2, n - 1);
}
else if(a <= 3*t){
dfs(n - 1, a - 2 * t, x, y);
x += qmi(2, n - 1);
y += qmi(2, n - 1);
}
else{
dfs(n - 1, a - 3 * t, x, y);
LL t = qmi(2, n - 1);
x = t + 1 - x;
y = t + 1 - y;
swap(x, y);
x += t;
}
}
int main(){
int T; cin >> T;
while(T--){
LL n, a, b; scanf("%lld %lld %lld", &n, &a, &b);
N = n;
LL x1, y1, x2, y2;
dfs(n, a, x1, y1);
dfs(n, b, x2, y2);
// cout << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << endl;
printf("%.0f\n", (sqrt((double)(x1 - x2) * (x1 - x2) * 100 + (double)(y1 - y2) * (y1 - y2) * 100)) );
}
return 0;
}
奇怪的汉诺塔
1. 三个柱子的汉诺塔,f[i]表示i个圆盘的移动次数,那么就是先将i-1个移动到b,代价是f[i-1],然后第i个移动到c上,
然再将b上的i-1个移动到c上,f[i] = 2*f[i-1] + 1;
2. 四个柱子的话,我们必然需要先移动i个盘子出来,此时的代价是在四塔模式下移动i个就是g[i]
然后在三塔模式下将剩下的n-i个移动到d上,然后将之前的i个在四塔模式下移动到b上
所以g[i] = min(d[i], 2*g[j] + f[i - j]); 枚举j就可以了
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL f[20], g[20];
int main(){
int n;
memset(g, 0x3f, sizeof g);
f[1] = g[1] = 1;
for(int i = 2; i <= 12; i ++) f[i] = 2 * f[i - 1] + 1;
for(int i = 2; i <= 12; i ++){
for(int j = 1; j < i; j ++) g[i] = min(g[j] + 2 * f[i] + 1, g[i]);
}
for(int i = 1; i <= 12; i ++) cout << f[i] << endl;
return 0;
}