算法竞赛进阶指南0x02 递推和递归

算法竞赛进阶指南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;
}

posted @ 2022-03-18 16:26  牛佳文  阅读(58)  评论(0)    收藏  举报