回归刷题记录(停更)

这篇博客探讨了算法中的位运算在快速幂、快速乘以及最短哈密顿路径等问题中的应用。作者通过实例展示了如何使用位运算优化计算,并提供了深搜模板在解决开关游戏问题中的解决方案。此外,还介绍了递归在指数型、组合型和排列型枚举问题中的应用。
摘要由CSDN通过智能技术生成

看到之前发过的博客还是费用流和点双边双问题,感慨良多呀。

10.15中午

洛谷P1226 【模板】快速幂

快速幂代码:

int power (int a, int b, int p) {
 int ans = 1 % p;
 for (; b; b >>= 1) {
  if (b & 1) ans = (long long)ans * a % p;
  a = (long long)a * a % p;
 }
 return ans;
}

这里引用一下高中时代膜拜的大佬之一syq大佬的“一行快速幂”

typedef long long ll;
ll fast(ll a,ll b){ll ans=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ans=mul(ans,a);return ans;}

CH0102 快速乘

快速乘有两种写法,第一种是借鉴快速幂,利用二进制进行运算;第二种是利用 a × b     m o d   p = a × b − ⌊ a × b / p ⌋ × p a\times b\ \bmod p =a\times b - \lfloor a\times b /p\rfloor \times p a×b modp=a×ba×b/p×p
代码如下:

long long mul (long long a, long long b, long long p) {
 a %= p; b %= p;
 long long c = (long double)a * b / p;
 long long ans = a * b - c * p;
 if (ans < 0) ans += p; else if (ans >= p) ans -= p;
 return ans;
}

syq大佬的一行快速乘

ll mul(ll x,ll y){return ((x*y-(ll)(((long double)x*y+0.5)/mod)*mod)%mod+mod)%mod;}

最短哈密顿路径

状态压缩DP题

memset (d, 0x3f, sizeof (d));
 d[1][0] = 0;
 for (int i = 1;i < 1 << n;i ++)
  for (int j = 0;j < n;j ++)
   if (i >> j & 1)
    for (int k = 0;k < n;k ++)
     if ((i ^ 1 << j) >> k & 1)
      d[i][j] = min (d[i][j], d[i ^ 1 << j][k] + a[k][j]);
 printf ("%d", d[(1 << n) - 1][n - 1]);

现在位运算真的不熟练。

POJ1995 Raising Modulo Numbers

也是快速幂的半裸题

10.16

洛谷P2114 [NOI2014]起床困难综合症

这道题目的题眼在于,找到位运算的特性——位之间相互独立。
那么只要对于每一个二进制位分开进行运算即可。
时间复杂度 O ( n log ⁡ m ) O(n \log m) O(nlogm)

const int maxn = 102000;
int calc (int a, int b, int s) {
 if (s == 1) return (a | b);
 if (s == 2) return (a ^ b);
 if (s == 3) return (a & b);
 return 0;
}
int n, m, op[maxn], t[maxn];
int main () {
 //freopen ("1.in", "r", stdin);
 n = read (), m = read ();
 for (int i = 1;i <= n;i ++) {
  char s[5];
  scanf ("%s", s); t[i] = read ();
  if (s[0] == 'O') op[i] = 1;
  if (s[0] == 'X') op[i] = 2;
  if (s[0] == 'A') op[i] = 3;
 }
 int ans = 0, attack = 0;
 for (int i = 29;i >= 0;i --) {
  int ans1 = 0, ans2 = 1;
  for (int j = 1;j <= n;j ++) {
   ans1 = calc (ans1, t[j] >> i & 1, op[j]);
   ans2 = calc (ans2, t[j] >> i & 1, op[j]);
  }
  if (ans + (1 << i) <= m && ans1 < ans2) {
   ans += (1 << i);
   attack += ans2 << i;
  }
  else attack += ans1 << i;
 }
 printf ("%d\n", attack);
 return 0;
}

10.18

0201 费解的开关

深搜模板题,给上题面。

描述
你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。
我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态
10111
01101
10111
10000
11011
在改变了最左上角的灯的状态后将变成:
01111
11101
10111
10000
11011
再改变它正中间的灯后状态将变成:
01111
11001
11001
10100
11011
给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。
输入格式
第一行有一个正整数n,代表数据中共有n个待解决的游戏初始状态。
以下若干行数据分为n组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。
对于30%的数据,n<=5;
对于100%的数据,n<=500。
输出格式
输出数据一共有n行,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。
对于某一个游戏初始状态,若6步以内无法使所有灯变亮,请输出“-1”。

基本思路

我能想到的一个基本思路就是:以行为阶段,第 i i i行如果点击完毕,那就不再去动它,如果要改变第 i i i行的状态,那就点击第 i + 1 i+1 i+1行的灯。
我们先枚举第一行所有的点击结果 ,对于每一种点击结果,接下来的点击方式都是固定的。也就是说我们需要从 2 5 2^5 25种可能性中找出最优解。
我们先利用一个深度优先搜索,枚举第一行所有的可能性;对于每一种可能性,再用另外一个深搜进行这一方案的完善。
如果操作到第五行发现还是没有达到要求,那我们可以断定无解。

int a[6][6], ans = 0;
void init () {
	for (int i = 1;i <= 5;i ++) {
		char t[5]; scanf ("%s", t);
		for (int j = 1;j <= 5;j ++)
			a[i][j] = t[j - 1] - 48;
	}
	ans = 10;
}
void change (int x, int y) {
	a[x][y] = 1 - a[x][y];
	if (x <= 4) a[x + 1][y] = 1 - a[x + 1][y];
	if (x >= 2) a[x - 1][y] = 1 - a[x - 1][y];
	if (y <= 4) a[x][y + 1] = 1 - a[x][y + 1];
	if (y >= 2) a[x][y - 1] = 1 - a[x][y - 1];
}
int check () {
	for (int i = 1;i <= 5;i ++)
		for (int j = 1;j <= 5;j ++)
			if (a[i][j] == 0) return 0;
	return 1;
}
void dfs (int i, int step) {//dfs函数用来完善第2行到第5行
	if (step > 6) return;
	if (i == 5) {
		if (check ()) ans = min (ans, step);
		return;
	}
	int t[10], p = 0;
	for (int j = 1;j <= 5;j ++)
		if (a[i][j] == 0) {
			change (i + 1, j);
			t[++ p] = j;
		}
	dfs (i + 1, step + p);
	for (int j = 1;j <= p;j ++)
		change (i + 1, t[j]);
}
int s0 = 0;//s0代表第一行共点击了几次
void f (int i) {//f函数用来枚举第一行
	if (i == 6)  {
		dfs (1, s0);
		return;
	}
	f (i + 1); change (1, i), s0 ++;
	f (i + 1); change (1, i), s0 --;
}
int main () {
	int T = read ();
	while (T --) {
		init (); s0 = 0; f (1);
		if (ans == 10) ans = -1;
		printf ("%d\n", ans);
	}
	return 0;
}

0301 递归实现指数型枚举

int n, c = 0, a[20];
void dfs (int x) {
	if (x > n) {
		for (int i = 1;i <= c;i ++)
			printf ("%d ", a[i]);
		printf ("\n");
		return;
	}
	dfs (x + 1); a[++ c] = x;
	dfs (x + 1); c --;
}

0302 递归/非递归实现组合型枚举

int n, m, c, a[50];
void dfs (int x) {
	if (c + n - x + 1 < m) return;//剪枝
	if (c == m) {
		for (int i = 1;i <= m;i ++)
			printf ("%d ", a[i]);
		puts ("");
		return ;
	}
	a[++ c] = x; dfs (x + 1);
	c--; dfs (x + 1);
}

0303 递归实现排列型枚举

int n, a[15], t[15];
void dfs (int c) {
	if (c == n) {
		for (int i = 1;i <= c;i ++)
			printf ("%d ", a[i]);
		puts (""); return ;
	}
	for (int i = 1;i <= n;i ++)
		if (t[i] == 0) {
			a[c + 1] = i;
			t[i] = 1;
			dfs (c + 1);
			t[i] = 0;
		}
}

啊这,接下来挑好题写题解了此文停更

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值