《算法竞赛进阶指南》0x00 递推与递归

复习常用的位运算操作
#include <bits/stdc++.h>
using namespace std;

int main() {
	int n, k;
	scanf("%d%d", &n, &k);
	
	//约定:整数n的二进制数,由低位 -> 高位依次为:0号位、1号位、2号位... 
	
	//取出k号位上的数字 
	printf("%d\n", (n >> k) & 1);
	
	//取出后k位:和k位1做按位与运算
	printf("%d\n", n & ((1 << k) - 1));
	
	//将k号位取反:k号位和1异或运算;其它位和0异或运算
	printf("%d\n", n ^ (1 << k));
	
	//将k号位赋值为1:k号位和1或运算;其它位和0或运算
	printf("%d\n", n | (1 << k));
	
	//将k号位赋值为0:k号位和0与运算;其它位和1与运算
	printf("%d\n", n & (~ (1 << k)));	 

	//统计非负整数n的二进制中1的个数
	int cnt = 0;
	for (int i = 0; i < 30; i ++) 
		if (n >> i & 1) 
			cnt ++;
	printf("%d\n", cnt);

	//统计非负整数n的二进制中1的个数
	cnt = 0;
	while (n) {
		cnt ++;
		n &= n - 1;
	}
	printf("%d\n", cnt);	

	return 0;
}
运算符的优先级别,从上到下以此是:
+, -
<<, >>
>, >=, <, <=, ==, !=
&
^
|
费解的开关
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int n, cnt;
int a[6][6], b[6][6];

//把第x行,第y列的灯按下 
void push(int x, int y) {
	cnt ++;
	b[x][y] ^= 1;
	b[x - 1][y] ^= 1;
	b[x + 1][y] ^= 1;
	b[x][y - 1] ^= 1;
	b[x][y + 1] ^= 1;
}

int main() {
	cin >> n;
	while (n --) {
		for (int i = 1; i <= 5; i ++) { 
			for (int j = 1; j <= 5; j ++) {
				char ch;
				cin >> ch;
				a[i][j] = ch - '0';
			}
		} 
		
		int minn = 7;
		for (int k = 0; k < (1 << 5); k ++) {
			memcpy(b, a, sizeof(a));
			cnt = 0;
			//枚举k对第1行的影响 
			for (int j = 0; j < 5; j ++) {
				int t = k >> j & 1;
				//把第1行第j+1盏灯按下 
				if (!t) push(1, j + 1);	 
			}
			
			//递推,依次检查第2~第5行,目的是使其正上方的灯变成1 
			for (int i = 2; i <= 5; i ++) {
				for (int j = 1; j <= 5; j ++) {
					if (!b[i - 1][j]) push(i, j);
				}
			} 
			
			//检验第5行是否全1 
			bool flag = true; 
			for (int j = 1; j <= 5; j ++) 
				if (!b[5][j]) {
					flag = false;
					break;
				}
			if (flag) minn = min(minn, cnt);
		}		
		
		if (minn == 7) cout << -1 << endl;
		else cout << minn << endl;
	}

	return 0;
}
黑白翻转棋
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 30;
int n, m, a[N], b[N], h[2000000];
int cnt, minn;

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			int t;
			cin >> t;
			a[i] = a[i] * 2 + t;	//把每一行的数字转换成一个m位二进制数 
		}
	}

	//预处理:把 0~2^m-1 这些整数的二进制表示中1的个数,预先处理出来 
	for (int i = 0; i < (1 << m); i ++) {
		for (int j = 0; j < m; j ++) {
			if (i >> j & 1) h[i] ++;
		}
	}

	minn = 1e9;		
	for (int k = 0; k < (1 << m); k ++) { //每一行有m位,即m位二进制 
		//初始化
		memcpy(b, a, sizeof(b));
		cnt = 0;
		
		b[0] = k;		//在矩形上面添加一行,就是枚举的m位二进制数 
		for (int i = 1; i <= n; i ++)  {
			//如果某一行某个位置是1, 则要翻转成0,统计要翻转的次数 
			cnt += h[b[i - 1]];
			
			b[i] = b[i] ^ b[i - 1];			//正下方 
			b[i] = b[i] ^ (b[i - 1] >> 1);	//右下方 
			b[i] = b[i] ^ (b[i - 1] << 1) & ( ~(1 << m));	//左下方
			b[i + 1] = b[i + 1] ^ b[i - 1];	//下下行 
		}
		
		if (b[n] == 0) {	//如果最后一行全0, 方案成功 
			minn = min(minn, cnt);
		}
	}
	
	if (minn == 1e9) cout << "Impossible";
	else cout << minn;

	return 0;
}
奇怪的汉诺塔
#include <bits/stdc++.h>
using namespace std;

int f[50];

int main() {
    memset(f, 0x3f, sizeof(f));
    f[0] = 0;

    for (int i = 1; i <= 12; i ++) {
        for (int j = 0; j < i; j ++) {
            f[i] = min(f[i], f[j] * 2 + (1 << (i - j)) - 1);
        }
        cout << f[i] << endl;
    }   

    return 0;
}
约数之和
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

int a, b, mod = 9901;

int qmi(int x, int y) {
	x %= mod;
	int res = 1;
	while (y) {
		if (y & 1) res = res * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return res;
}

//  if (k & 1)
//  p^0 + p^1 + p^2 + ... + p^k		
//= (p^0 + p^1 + ... + p^(k/2)) + (p^(k/2+1) + p^(k/2+2) + ... + p^k)
//= (p^0 + p^1 + ... + p^(k/2)) * (1 + p^(k/2+1))
int sum(int p, int k) {
	if (k == 0) return 1;
	if (k & 1) return sum(p, k / 2) % mod * (1 + qmi(p, k / 2 + 1)) % mod;
	return (1 + p % mod * sum(p, k - 1) % mod) % mod;
}

int main() {
	cin >> a >> b;
	
	int ans = 1;
	for (int i = 2; i <= a; i ++) {
		int cnt = 0;
		while (a % i == 0) {
			cnt ++;
			a /= i;
		}
		// a有cnt个因子i  ->  i^cnt | a  ->  i^(cnt*b) | a^b 
		if (cnt) ans = ans * sum(i, cnt * b) % mod;
	}
	
	if (a == 0) ans = 0;
	cout << ans << endl;
	
	return 0;
}
铺设地砖 https://iai.sh.cn/problem/394
#include <iostream>
using namespace std;

const int N = 1e5+10, MOD = 1e9+7;
int n;
long long a[100005] = {1, 2};

int main() {
	cin >> n; 
	for (int i = 2; i <= n; i ++) {
		a[i] = (a[i-1]*2 + a[i-2]*3) % MOD;
	}

	cout << a[n];

	return 0;
}
再铺地砖 https://iai.sh.cn/problem/407
#include <iostream>
using namespace std;

const int N = 1e5+10, MOD = 1e9+7;
int n, a[N] = {1,2}, sum = 4;

int main() {
	cin >> n;
	for (int i = 2; i <= n; i ++) {
		a[i] = (sum + a[i-2] + 2) % MOD;
		sum = (sum + a[i]) % MOD;
		sum = (sum + a[i]) % MOD;
	}
	cout << a[n];

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值