【题解】StarryCoding P211 勇闯高塔

题目传送门:P211 勇闯高塔 | StarryCoding算法竞赛平台

题目描述

很久很久以前,宇宙混沌一片,一天,一位神祇出现,凭借一把巨大的神斧将混沌撕裂,混沌渐渐分开,形成了天和地。在那之后,神祇并没有把祂的开天斧带走,那只巨斧被留在了地面上,化形成了一座高塔。

传说中,在高塔的顶端,遗留着开天神的宝藏,关于宝藏,有的人说是无数的金银珠宝,有的人说是绝世神斧,众说纷纭,但是宝藏的真面目,没人看到过,也没人知道。但是人们清楚的是,在高塔内,有长期待在里面的生物吸收了开天斧留下的灵气,作为了守护神守卫着高塔的宝藏。

这天,有一位勇者为了看清宝藏的真面目,闯进了高塔内, n n n个血量为 a a a的怪物出现在了勇者面前。

勇者有两个技能:

  • 血量交换:选择 i i i j j j b b b,交换 a i a_i ai a j a_j aj的二进制表示中的第 b b b个数字。
  • 斩击:发出一次斩击。造成一次值为 max ⁡ ( a ) − min ⁡ ( a ) \max(a) - \min(a) max(a)min(a)的伤害。

勇者希望节省力气,尽量少的发动斩击,而血量交换因为不消耗体力,可以执行任意次。

求发动一次斩击能达到的最大伤害量()。

在二进制表示中,位从右(最低有效)到左(最高有效)编号。考虑到任何二进制表示形式的开头都有无限数量的前导零位。

例如,将 4 = 10 0 2 4=100_2 4=1002 3 = 1 1 2 3=11_2 3=112的第 0 0 0位交换将得到 10 1 2 = 5 101_2=5 1012=5 1 0 2 = 2 10_2=2 102=2。将 4 = 10 0 2 4=100_2 4=1002 3 = 1 1 2 3=11_2 3=112的第 2 2 2位交换将得到 00 0 2 = 0 2 = 0 000_2=0_2=0 0002=02=0 11 1 2 = 7 111_2=7 1112=7

其中, max ⁡ ( a ) \max(a) max(a)表示数组 a a a的最大元素, min ⁡ ( a ) \min(a) min(a)表示数组 a a a的最小元素。

x x x的二进制表示是用基数 2 2 2编写的 x x x。例如,以 2 2 2为基数编写的 9 9 9 6 6 6分别为 1001 1001 1001 110 110 110

输入描述

第一行包含一个整数 t t t ( 1 ≤ t ≤ 128 1 \le t \le 128 1t128 ) — 测试用例的数量。

每个测试用例的第一行包含一个整数 n n n ( 3 ≤ n ≤ 512 3 \le n \le 512 3n512 ) — 数组 a a a 的长度。

每个测试用例的第二行包含 n n n 个整数 a 1 , a 2 , … , a n a _ 1, a _ 2, \ldots, a _ n a1,a2,,an ( 0 ≤ a i < 1024 0 \le a _ i \lt 1024 0ai<1024 ) — 数组 a a a 的元素。

确保所有测试用例的 n n n 之和不超过 512 512 512

输出描述

对于每个测试用例,打印一个整数 - max ⁡ ( a ) − min ⁡ ( a ) \max(a) - \min(a) max(a)min(a) 的最大可能值。

输入样例1

4
3
1 0 1
4
5 5 5 5
5
1 2 3 4 5
7
20 85 100 41 76 49 36

输出样例1

1
0
7
125

思路

对于最大的元素,我们希望它的每一位都尽可能地设置为 1 1 1;对于最小的元素,我们希望它的每一位都能尽可能地设置为 0 0 0

因为是无限制地交换,对于二进制中某一位上的数字,我们都可以通过两两交换换到另一个数字上。换句话说,对于二进制中某一位上的数字,只要有 0 0 0出现,我们都可以将它换到我们所需的最小数字的二进制对应位置上面。对于最大数所需的 1 1 1也是如此。

因为数据范围很小,本题解法较多。这里介绍写法相对简单的一种。我们可以利用与运算有 0 0 0 0 0 0的性质,通过对所有元素两两进行与运算得到最小数;利用或运算有 1 1 1 1 1 1的性质,通过对所有元素两两进行或运算得到最大数。最后相减得到答案。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 1000;

int a[N];

void solve()
{
    int n; cin >> n;
    for(int i = 0; i < n; ++i) cin >> a[i];
    int mi = a[0], ma = a[0];
    for(int i = 1; i < n; ++i)
    {
        mi &= a[i];
        ma |= a[i];
    }
    cout << ma - mi << '\n';
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int _; cin >> _;
    while(_--) solve();
    return 0;
}

本题由codeforces上的1763A改编而成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值