本文主要是介绍《算法竞赛进阶指南》起床困难综合症,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
输入描述:
第 1 行包含2 个整数,依次为 𝑛, 𝑚 ,表示有 𝑛 扇防御门,初始攻击力为 0 到 𝑚 之间的整数。 接下来 𝑛 行,依次表示每一扇防御门。每行包括一个字符串 op 和一个非负整数 𝑡,两者由一个空格隔开,且 op 在前, 𝑡 在后,op 表示该防御门所对应的操作,𝑡 表示对应的参数。
输出描述:
输出一行一个整数,表示atm 的一次攻击最多使 drd 受到多少伤害。
输入
3 10
AND 5
OR 6
XOR 7
输出
1
说明:
atm 可以选择的初始攻击力为 0,1, … ,10。
假设初始攻击力为 4,最终攻击力经过了如下计算
4 AND 5 = 4
4 OR 6 = 6
6 XOR 7 = 1
类似的,我们可以计算出初始攻击力为 1,3,5,7,9 时最终攻击力为 0,初始攻击力为 0,2,4,6,8,10 时最终攻击力为 1,因此atm 的一次攻击最多使 drd 受到的伤害值为 1。
题目大意:
给定 n,m 以及 n 个数和该数所对应的运算,其中运算有 与、或、异或 三种,问在所有不大于 m 的非负整数中,对给定的 n个数都按该数所对应的运算运算一遍后,能得到得最大的值是多少。
AND 表示按位与,OR 表示按位或,XOR 表示按位异或
解题方法:
注意到 按位与、按位或、按位异或 共有的一个性质:每次运算只有关该位上的数,不影响其它位上的数。
所以我们可以像最大异或对这题一样,从高位到低位来确定数的每一位。如果该位可以填 u,并且填 u 之后答案的该位是 1,那么在该位填 u,否则填 !u
那么如何判断该位能填几呢?
如果该位填 1 后,所得到的数大于 m,那么该位填 !u,否则如果该位填 1 后,所得到的数对 n 个数都运算之后,结果小于等于该位填 0 后得到的结果,那么为了让剩下能填的数更大,该位填 0,否则该位填 1
由于我们只需要得到填出来的数对所有数运算后的结果,而并不需要输出填出来的数,所以在写代码的时候并不需要真正的把数填出来,只需要确定是否能将答案的该位填成 1 即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int n, m; // n, m 即题目描述中 n, m
int ans; // ans 存我们能得到的最大的答案
int t[N]; // t 存输入的 n 个数
short op[N]; // op 存 n 个数对应的操作,1 表示按位或,2 表示按位异或,3 表示与
char str[4]; // str 用于读入操作bool calc(bool x, int j) // calc 用于计算 x 经过所有数的第 j 位操作后所得到的结果
{for (int i = 0; i < n; i ++ ) // 从 0 到 n 枚举所有读入的数与其对应操作if (op[i] == 1) x |= t[i] >> j & 1; // 如果 op[i] 为 1,说明该数所对应的运算为按位或else if (op[i] == 2) x ^= t[i] >> j & 1; // 如果 op[i] 为 2,说明该数所对应的运算为按位异或else x &= t[i] >> j & 1; // 如果 op[i] 为 3,说明该数所对应的运算为按位与return x;
}int main()
{scanf("%d %d", &n, &m);for (int i = 0; i < n; i ++ ){scanf("\n%s %d", str, t + i);if (*str == 'O') op[i] = 1; // 如果该操作为 OR ,那么 op[i] 制为 1else if (*str == 'X') op[i] = 2; // 如果该操作为 XOR,那么 op[i] 制为 2else op[i] = 3; // 否则该操作为 AND,那么 op[i] 制为 3}for (int i = 29; ~i; i -- ) // 因为本题中 m 最大是 10 ^ 9,log2(10 ^ 9) = 3log2(10 ^ 3) < 3 * 10 = 30,所以每次 i 从 29 往后枚举就可以了if (1 << i <= m) // 如果填 1 后小于等于 m,要看填完后对答案的影响来填{bool x = calc(0, i), y = calc(1, i); // 先分别处理出该位填 0 的结果和该位填 1 的结果if (x >= y) ans|= x << i; // 如果该位填 1 并不比该位填 0 更优,那么为了让剩下能填的数更大,在该位填 0else ans |= y << i, m -= 1 << i; // 否则在该位填 1,填完后让 m 减去该位填 1 的结果,这样在后面填数的时候只用考虑是否大于 m 就可以了}else ans |= calc(0, i) << i; // 否则该位只能填 0,printf("%d\n", ans);return 0;
}
这篇关于《算法竞赛进阶指南》起床困难综合症的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!