7.19 搜索笔记

题单链接
定期复习。

T1

题目
• 给定一个 N 个点 (编号 1N ),M 条边的图。
• 请你找出若干个环,满足:
1. 这些环没有重复的边。
2. 这些环覆盖了所有的点和边。
3N,M50000

Solution:

找环模板题,dfs + 栈即可

T2

题目

• 给定一个 1N 的排列 P
• 你需要构造一个括号序列,满足以下条件:
1. 括号序列是合法的。
2. 若第 i 个括号是左括号,则将 iPi 连边,使得最后形成的图满足所有点的度均为 1
• 保证有解。
N=20,40,100

Solution

• 因为保证有解,所以排列 P 形成的图一定是个
• 由于括号序列要合法,所以对于每一个环,我们选择了一条边,相邻的边就不能选,所以每个环只有两种情况。
• 考虑最坏情况,每个环仅有两个点,那么时间复杂度为 O(250),明显不能通过此题。
• 考虑优化。
• 如何判断括号序列是否合法?前缀左括号数量 前缀右括号数量。
• 对于两个点一个环的情况,我们将编号小的直接作为左括号,编号大的作为右括号。
• 那么,只存在节点个数 4 的环,复杂度降为 O(225) ,可以通过此题。

T3

题目

• 给定一个 NM 的矩形色板,有 K 种不同的颜料,有些格子已经填上了某种颜色。
• 现在需要将其他格子也填上颜色,使得从左上角到右下角的任意路径经过的格子都不会出现两种及以上相同的颜色。
• 路径只能沿着相邻的格子,且只能向下或者向右。
• 计算所有可能的方案,结果对 1e9+7 求模。
1N,M1000,K<20

Solution

• 搜索技巧题。
n,m1000的数据明显是假的,k 仅有 20,由于路径中不能出现两种以上的颜色,所以当 n+m1>k 时明显无解。
• 数据马上降了下来,此时考虑搜索。
• 普通的搜索明显不能通过,我们需要进行优化。
• 我们用状压的方式记录颜色,接着对于每个点,暴力枚举 k 种颜色。
• 但是这样还是不够,我们需要加上两个显而易见的剪枝。
1. 当剩余的格子 > 剩下的颜色时,退出。
2. 如果当前还有 x 种颜色没有被使用,那么对于一个空白格,在此格子涂上任意一种颜色,对答案的贡献都是 相同 的,此时只需要涂上任意一种,最后累加即可。

ps : 复习

T4

题目

• 给定 D,求满足 rev(N)=N+DN 的个数,rev(N) 表示十进制下将 N 按位翻转并去掉前导 0 后的数。
1D10

Solution

dfs + 柿子题。
先设 |n|n 的位数。
n 翻转后,我们直接将前后的位置匹配算贡献。枚举每对 高位减低位 的差,就能算出 rev(n)n 的值,如果与 D 相等把方案数计入答案即可。
上述算法不能通过此题,所以我们考虑剪枝优化。
我们枚举对时,按贡献从高到低枚举,偏差很难由低位补足。也就是说,满足条件的差的序列很少。这时可行性剪枝效果非常出色。我们只需要加一个可行性剪枝即可通过。

int d, ans;

int po[N], to[N][N], suf[N][N];

int dfs(int all, int now, int sum, int res)

{
	if (sum + suf[all][now] < d || sum - suf[all][now] > d) return 0;
	if (now == (all >> 1))
	{
		if (abs(d - sum) % (po[all - now] - po[now - 1]) || abs(d - sum) / (po[all - now] - po[now - 1]) > 9) return 0;
		int temp = (d - sum) / (po[all - now] - po[now - 1]);
		return res * (10 - abs(temp) - (now == 1));
	}
	int ret = 0;
	for (int i = (-9); i <= 9; i ++ )
	{
		if (now == 1 && i == -9) continue;
		int nwp = (po[all - now] - po[now - 1]) * i;
		ret += dfs(all, now + 1, sum + nwp, res * (10 - abs(i) - (now == 1)));
	}
	return ret;
}

void solve()

{
	po[0] = 1;
	for (int i = 1; i <= 18; i ++ )
	{
		po[i] = po[i - 1] * 10;
	}
	for (int i = 2; i <= 18; i ++ ) 
	{
		for (int j = 1; j <= (i >> 1); j ++ )
		{
			to[i][j] = po[i - j] - po[j - 1];
		}
	}
	for (int i = 2; i <= 18; i ++ )
	{
		for (int j = (i >> 1); j >= 1; j -- ) suf[i][j] = suf[i][j + 1] + to[i][j] * 9;
	}
	d = read();
	for (int i = 2; i <= 18; i ++ )
	{
		if (i & 1) ans += 10 * dfs(i, 1, 0, 1);
		else ans += dfs(i, 1, 0, 1);
	}
	cout << ans;
} 

ps : 没怎么听懂,需要复习

T5

题目

• 我方有 n 个士兵,敌方有 m 个士兵,每个士兵的血量在 16 之间。
• 现在有 d 轮攻击,每轮攻击会等概率选择一个活着的士兵,使得血量 1 ,如果血量为 0 ,则表示死亡。
• 问敌方士兵全部死亡的概率是多少?
1n,m51d100

Solution

咕咕咕。

T6

题目

• 有一个监狱,关押着两个犯人。
• 监狱可以看做 hw 的矩阵,包含四种类型:
1. . 表示空地,可以行走。
2. * 表示墙,无法行走。
3. # 表示门,初始时时关闭,但可以打开然后行走。
4. S 表示犯人的位置。
• 现在,两个犯人要逃出监狱 (走出矩阵),为了避免打草惊蛇,他们需要尽可能的少开门逃走。
• 问,两个犯人逃出监狱至少要打开多少个门。T 组测试数据。
2h,w100T100

Solution

咕咕咕。

T7

题目

• 有 n 个任务和三个人 L,M,W
• 每次任务给出每个人参与后能得到的分数,每次任务需要两个人完成。
• 为了保证 n 个任务结束后三个人得到的分数是一样的,且尽量大。请你输出每次任务要派哪两个人,如果不行,输出impossible
n20

Solution

• 数据不大,先考虑爆搜,最坏复杂度为 O(325),显然不能接受。
• 那么考虑优化,Meet In The Middle
• 我们先搜索前半部分,设前半部分搜索三个人的分数分别为 abc,后半部分搜索三个人的分数分别为 xyz,那么必定满足 a+x=b+y=c+z,转化得 ac=zxab=yx
• 因此,搜索前半部分记录 (ac,ab),相同的记录 a 更大的情况。搜索后半部分,查找 (zx,yx) 是否出现过,出现则更新。时间复杂度降到 O(3n2)
• 我们可以用三进制来记录答案,最后输出时对 3 不断取模输出即可。

T8

题目

• 给定一个长度为 n 的正整数数组 Ai
• 求是否存在 x ,使得对于数组 Bi,其中 Bi=AixBi 中所有元素两两 popcount(Bi) 相同。
• 其中 是按位异或,popcount(x)x 的二进制表示中 1 的个数。
• 请输出任意满足条件的 x ,若不存在输出 1
2n100, 0Ai, x2301

Solution

• 与上题思路类似,同样是折半搜索。
• 将 xai 的二进制都拆分为前 15 位和后 15 位。
• 设 bi 表示 x15 位与 ai15 位异或后 1 的数量。
• 设 ci 表示 x15 位与 ai15 位异或后 1 的数量。
• 需要满足所以 bi+ci 相等,即 b1+c1=b2+c2==bi+ci
• 只考虑 b1+c1=bi+ci,与上题类似,转换柿子,得 bib1=c1ci
• 求解 bi 时,将每一个 x 对应的 bib1 都存储下来,求解 c 时,将每一个 x 对应的 c1ci 都存储下来。
• 如果发现某个 x ,每一项对应的 bib1 和 $c_1 − ci 均相等,则找到了解。
• 时间复杂度为 O(15n215)

posted @   恋暗  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示