0
点赞
收藏
分享

微信扫一扫

Ancient Civilization

承蒙不弃 2022-01-16 阅读 54
算法

Ancient Civilization

CodeForces Round #765 (Rated for div. 2) Problem A

题目描述

给定n个二进制数 x 1 , x 2 , ⋯   , x n x_1, x_2, \cdots, x_n x1,x2,,xn,每个二进制数的长度是l,求出一个长度也为l的二进制数y,使得 ∑ i = 1 n d i s ( x i , y ) \sum_{i=1}^{n}dis(x_i, y) i=1ndis(xi,y)最小,即y到所有数的距离之和最小。

其中两个数之间的距离指的是两个二进制数每个位置上的数码不同的个数。例如 d i s ( 10010 , 11011 ) = 2 dis(10010, 11011) = 2 dis(10010,11011)=2,因为这两个数在第2位和第5位不同,一共是两个位置的数不同,所以距离是2。

样例输入

第一行输入一个整型的t(1 <= t <= 100),表示有t个测试例子。

对于每个测试例子,第一行输入两个整型nl(1 <= n <= 100, 1 <= l <= 30),表示输入n个数字,每个数字的二进制形式的长度为l。第二行输入n个由空格分隔的数字 x i ( 0 < = x i < = 2 l − 1 ) x_i(0 <= x_i <= 2^l-1) xi(0<=xi<=2l1)

7
3 5
18 9 21
3 5
18 18 18
1 1
1
5 30
1 2 3 4 5
6 10
99 35 85 46 78 55
2 1
0 1
8 8
5 16 42 15 83 65 78 42

样例输出

一共输出t行,每行输出对应的那个y的值(十进制表示)。

17
18
1
1
39
0
2

思路

这是一个最小化问题,关键在于搞清楚那个距离的含义。

这个距离是指两个二进制数有差别的位置的个数,我们要最小化这一差异。这个描述过于抽象,最好是以公式的形式定量来表达。考虑到是二进制,其实某个位置上的数字如果相同,那么他们的差的绝对值就是0,如果不同,那么差的绝对值就是1。所以为了差异最小,只要每一位的差的绝对值最小。

一共有n个数,要使得差异的总和最小化。

x i j x_{ij} xij表示数字 x i x_i xi的第 j j j位数字, y j y_j yj表示数字 y y y的第 j j j位数字(最低位为第0位)。因此我们就是要最小化
∑ i = 1 n ∑ j = 0 l − 1 ∣ y j − x i j ∣ \sum_{i=1}^{n} \sum_{j=0}^{l-1} |{y_j-x_{ij}}| i=1nj=0l1yjxij
因为求和符号可以交换顺序,所以也就是要最小化这个东西:
∑ j = 0 l − 1 ∑ i = 1 n ∣ y j − x i j ∣ \sum_{j=0}^{l-1}\sum_{i=1}^{n}|{y_j-x_{ij}}| j=0l1i=1nyjxij
所指只需要看这一部分:
∑ i = 1 n ∣ y j − x i j ∣ \sum_{i=1}^{n}|{y_j-x_{ij}}| i=1nyjxij

也就是说,只要y的每一位都和 x i x_i xi的每一位的差值加起来最小即可。方法就是随大流。

这么讲还是有点抽象,直接看个例子。

以样例的第一组数据为例:
x 1 = 10010 x 2 = 10101 x 3 = 01001 x_1 = 10010 \\ x_2 = 10101 \\ x_3 = 01001 \\ x1=10010x2=10101x3=01001
我们来考虑y的最低位应该是什么,假设为 y 0 y_0 y0。观察到这三个数的最低位分别是0 、1 、 1,那么 y 0 y_0 y0和这三个数的差值要最小,而且 y 0 y_0 y0只能取0或1。所以 y 0 y_0 y0只要取个数多的那个,在这里就是取 1。其他位的数字同理,第1位三个数分别是 1 、 0、 0,所以取0;第二位三个数分别是0 、1、 0,所以取0;第3位三个数分别是0、 0、 1,所以取0;第4位三个数分别为1、 1、 0,所以取1。最后y的值就是10001,也就是17。

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int tt;
	cin >> tt;
	while(tt--) {
		int n, l;
		cin >> n >> l;
        //二维数组,l行2列,用来记录每个位置上0和1的个数
		vector<vector<int>> cnt(l, vector<int>(2, 0));
		for(int i = 0; i < n; ++i) {
			int x;
			cin >> x;
			for(int j = 0; j < l; ++j) {
                //位运算取出每一位的数
				int bit = (x >> j) & 1;
				cnt[j][bit] += 1;
			}
		}
		int y = 0;
		for(int i = 0; i < l; ++i) {
			if(cnt[i][1] > cnt[i][0]) {
				y += 1 << i;
			}
		}
		cout << y << "\n";
	}
	return 0;
}
举报

相关推荐

0 条评论