在编写程序解决某些问题时,可以灵活地使用进位制数,例如像二进制枚举就是灵活使用二进制数。下面再讲述一些例题。
【例1】至少一位数字相同
问题描述
给定N个正整数A1,A2,...,AN,求有多少整数对(i,j),满足以下条件:
1≤i<j≤N,Ai和Aj至少有一位数字是相同的(不一定要在相同的数位)。
输入
输入的第一行包含一个正整数N。
接下来N行,每行包含一个正整数Ai。
输出
输出一行一个整数,表示满足条件的整数对的数目。
输入样例
4
32
51
123
282
输出样例
4
(1)编程思路。
以样例为例说明。共有4组整数对满足条件。(32,123)、(32,282)、(51,123)和(123,282)。
显然,若采用二重循环两两组合来判断每组数中是否至少有一位数字是相同的,在数据量较大的情况下,不是一个可取的方法。
实际上要判断两个整数是否至少有一位数字是相同的。我们是不在乎两个数的每一个数位是什么、哪几个位上的数字相同,只用关心0~9这9个数字中是否有某个数字在两个数中都出现过,若都出现过,则两个数至少有一位数字是相同的。
由于十进制中只有0~9共10个数码,因此可以用一个10位的二进制数来表示一个十进制整数X的类型,这个二进制数的第k(0≤k≤9)位为1表示整数X中含有数字k,若数字k在整数X中没出现,则对应二进制数的第k位为0。
用数组a来保存各类型整数的个数,显然任何一个正整数的类型一定在 [0,1023]之间。即数组a最多有1024个元素。初始时,数组a的元素值全部置为0。
还是以样例中的4个数为例。
整数32中含有2、3这两个数字,对应二进制数为0000001100,数的类型号为12,a[12]加1,a[12]=1。
整数51中含有1、5这两个数字,对应二进制数为0000100010,数的类型号为34,a[34]加1,a[34]=1。
整数123中含有1、2、3这3个数字,对应二进制数为0000001110,数的类型号为14,a[14]加1,a[14]=1。
整数282中含有2、8这两个数字,对应二进制数为0100000100,数的类型号为260,a[260]加1,a[260]=1。
这样处理后,再求N个正整数中满足要求的整数对的数量ans(初值为0)就很方便了。分两种情况处理。
1)两个整数的类型相同,则同类型中任意取两个数就满足要求。用循环对a[0]~a[1023]中各a[i]值进行遍历。若 a[i]!=0,则 ans=ans+a[i]*(a[i]-1)/2。
2)两个整数的类型不同。设一个类型为i,一个类型为j (设i<j),若 i & j的值不为0,则i和j对应的二进制数一定在某个位上都是1,也就是存在相同的数字(某个位都为1)。这样,a[i]和a[j]中各取1个数就满足要求。 ans=ans+a[i]*a[j]。
(2)源程序。
#include <stdio.h>
int main()
{
int n;
scanf("%d",&n);
int i,j;
long long a[1024]={0};
int min=1024,max=0;
for (i=1;i<=n;i++)
{
long long x;
scanf("%lld",&x);
int h[10]; // 记录0~9每个数字在x中是否出现
for (j=0;j<10;j++) h[j]=0;
while(x)
{
h[x%10]=1; // 数字x%10出现了,h[x%10]记为1
x/=10;
}
int num=0;
for (j=0;j<10;j++)
{
num=num*2+h[j]; // 将h[0]~h[9]中保存数据作为二进制数转换为十进制数num
}
a[num]++; // num这种数增加1个
if (num>max) max=num;
if (num<min) min=num;
}
long long ans=0;
for (i=min;i<=max;i++) // 同一种数内两两组合
ans+=a[i]*(a[i]-1)/2;
for (i=min;i<max;i++) // 不同种类的数两两组合
{
for (j=i+1;j<=max;j++)
if (i & j) ans+=a[i]*a[j];
}
printf("%lld\n",ans);
return 0;
}
将上面的源程序提交给洛谷题库 P7617 [COCI2011-2012#2] KOMPIĆI (https://www.luogu.com.cn/problem/P7617),可以Accepted。
【例2】异或和
问题描述
给定一个长度为N的序列A1,A2,...,AN,求序列元素两两异或的总和。
例如,某序列中有3个数,A1=7,A2=3,A3=5。
则有 A1 ⊕ A2 = 4,A1 ⊕ A3 = 2,A2 ⊕ A3 = 6,
4 + 2 + 6 = 12,因此序列元素两两异或的总和为12。
输入
输入的第一行包含一个正整数N(1≤N≤106)。
接下来N行每行包含一个正整数Ai(1≤Ai≤106)。
输出
输出一行一个整数,表示两两异或后的总和。
输入样例
3
7
3
5
输出样例
12
(1)编程思路。
若通过二重循环对序列中的数据进行两两组合求异或和,在数据量大的情况下肯定是超时的。
首先,我们考虑一个数转成二进制后每个位的操作,每个位上的数据只能是0或1,其异或运算规则是: 0和1 异或得1 , 1和1 或者 0和 0 异或得0 。怎么求多个0 和1 的两两异或和呢?
举个例子: 0,1,0 三个数两两异或和应该是:0 异或 1 加上 0 异或0 再加上 1 异或 0,和值sum=1+0+1=2 。从中可以发现,每个 0 和一个 1 进行异或,和sum 就要加 1 ,也就是说每一个 0 都会使 sum 加上 1 的个数(因为 0 要和 1 的个数个 1 异或)。设在n个0、1序列中有x 个1 ,则有 n-x个0,这样两两异或的和值sum 就等于 0 的个数乘 1 的个数,也就是 sum=(n−x)*x 。
现在要对N个正整数序列求两两异或和。具体做法是:
将原序列中的每个元素都转换为二进制,并用一个数组 a 记录序列中全部元素各二进制数中每一位1 的个数。最后用一个循环,将二进制每一位的两两异或和都算出来,累加到和值sum中。
n 个数中第i 位上每一对 0 和 1 都能造成 2i 的贡献。在n个数中,已求出各二进制数第i位上有a[i]个 1,有 n-a[i]个 0,而每个 0都和a[i]个 1 都能造成2i的贡献,所以
sum=sum+a[i]*(n−a[i])*2i 。
以样例为例说明。7 对应二进制数为 111,因此,a[0]=a[0]+1,a[1]=a[1]+1,a[2]=a[2]+1,由此,a[0]=1,a[1]=1,a[2]=1。
3 对应二进制数为 11,因此,a[0]=a[0]+1,a[1]=a[1]+1,由此,a[0]=2,a[1]=2,a[2]=1。
5 对应二进制数为 101,因此,a[0]=a[0]+1,a[2]=a[2]+1,由此,a[0]=3,a[1]=2,a[2]=2。
为此,在异或和的和值sum中, 第0位贡献 3*0*1=0,第1位贡献 2*(3-2)*2=4,第2位贡献 2*(3-2)*4=8。因此,和值sum=0+4+8=12。
(2)源程序。
#include <stdio.h>
int main()
{
int n;
scanf("%d",&n);
int i,a[25]={0},len=0;
for (i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
int k=0;
while(x) // 将x转为二进制
{
a[k]=a[k]+x%2; // 如果第k位是1,则a[k]加1
x/=2;
k++;
}
if (len<k) len=k; // 各数对应二进制数的最长位数
}
long long ans=0;
for (i=0;i<len;i++)
ans+=1ll*a[i]*(n-a[i])*(1<<i);
printf("%lld\n",ans);
return 0;
}
【例3】天平称重
问题描述
给你一台天平,一件货物重m公斤。然后给你一些重1,3,9,27,…,3^k的砝码。当然,不同权重的砝码数量只有一个。
现在把货物放在天平的左边。然后你应该在天平的两边放一些砝码,使天平平衡。
输入
整数m表示货物的重量(0≤ m≤ 100 000 000)
输出
您应该输出两行。
在第一行中,第一个整数N1是放在天平左边的砝码的数量,然后N1个整数(按升序),表示放在左边的各砝码的重量,每个数用一个空格隔开。
在第二行中,请使用与第一行相同的方式输出N2,即右侧放置的砝码数。然后,按照升序排列的N2个整数表示放在右边的各砝码的重量。
输入样例
42
30
输出样例
3 3 9 27
1 81
0
2 3 27
(1)编程思路。
可以看出砝码重量1、3、9、27、…正好是一个三进制数各位上的权值,因此应考虑三进制数的应用。
称重时砝码可以放在左盘(物体盘),也可以放在右盘(砝码盘)。若砝码只放在右盘,则 物体质量=砝码盘砝码质量;若右盘和左盘中都放置了砝码,则 物体质量=右盘砝码质量-左盘砝码质量。这样,由于可以把砝码加在天平的左盘中,因此,放在左盘中的砝码不是要加在称出的质量上面,而是要从中减去的数。例如,5=9-3-1、6=9-3、7=9+1-3等等。
为了达到这个目的,设所用的三进制数码不是通常的0、1、2,而是-1、0、1。即2可以写成3-1,将其转化成-1这个数字。为了描述简便,把-1写成i,以后只要在三进制中碰到2这个数字,就把它改写成1i(即3-1=2)。例如,三进制中的22102这个数,可以用下面的方法改写成10i11i。
22102 = 20000 + 2000 + 100 + 2 = 1i0000 + 1i000 +100 + 1i
= 1i0000 + 1i000 +11i = 1i0000 + 1i11i = 10i11i
来看几个实际克数的称重情况。
例如,为了称出14克,先将14化成普通三进制112,再进行改写,112=100+10+1i=100+2i=100+20+i = 100 +1i0 +i =100 +1ii = 2ii =1iii。这就是说,把27这块砝码放进右盘,而把9、3、1三块砝码放进左盘中,就可以称出14克出来(27-9-3-1=14)。
再看怎样称出26克来,26化成普通三进制222,进行改写,222=1i00+1i0+1i=1i00+10i=100i。这就是说,把27这块砝码放进右盘,而把1这块砝码放进左盘中,就可以称出26克出来(27-1=26)。
因此,本题的处理办法是:
先将输入的十进制数n转换为3进制数,转换后得到的各位三进制数字保存在数组a中。然后对数组a中的值从低位向高位进行校正。校正方法为:
若 a[i]的值为2,则变为 -1, 同时 a[i+1]加1(相当于向前1位进位);
若 a[i]的值为3,则变为 0, 同时向前1位进位,即 a[i+1] 加1;
若 a[i]的值 为0 或 1 时保持不变。
之后将a[i]值为1所对应的重为3i的砝码放在右盘中,a[i]值为-1 所对应的重为3i的砝码放在左盘中,就是问题的答案。
(2)源程序。
#include <stdio.h>
int main()
{
int table[21]; // table[i]保存3的i次方的值
table[0]=1;
int i;
for (i = 1; i < 20; i++) // 预先求得3的1次方~3的19次方值保存到数组table中
table[i]=table[i-1]*3;
int n;
while (scanf("%d", &n)!=EOF)
{
int len = 0;
int a[21];
while (n!=0) // 将n转换为3进制数,并将各位数字保存到数组a中,a[0]保存最低位数字
{
a[len++] = n % 3;
n = n / 3;
}
a[len]=0; // 最高位的前面先补个0
int lcnt=0,rcnt=0; // 分别保存放在天平左端和天平右端的砝码个数
for (i=0;i<len;i++) // 从低位到高位对3进制数进行校正
{
if (a[i]==1) rcnt++;
if (a[i]==2)
{
a[i]=-1; a[i+1]++; lcnt++;
}
if (a[i]==3)
{
a[i]=0; a[i+1]++;
}
}
if (a[len]!=0) { rcnt++; len++; }
printf("%d",lcnt);
for (i=0;i<len;i++)
if (a[i]==-1) printf(" %d",table[i]);
printf("\n");
printf("%d",rcnt);
for (i=0;i<len;i++)
if (a[i]==1) printf(" %d",table[i]);
printf("\n");
}
return 0;
}
将上面的源程序提交给 HDU 3029 Scales (http://acm.hdu.edu.cn/showproblem.php?pid=3029),可以Accepted。
关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭8年前。Improvethisquestion我需要实现具有各种灵活需求的密码安全。这些要求基本上取自Sanspasswordpolicy:Strongpasswordshavethefollowingcharacteristics:Containatleastthreeofthe
这道题开始于here.但随着我对雷神的了解越来越多,情况发生了很大变化。我正在尝试创建一个带参数的Thor::Group子命令。奇怪的是,如果没有参数,它就可以工作。我可以使用Thor::Group作为子命令吗?这在我输入时有效:foocounterfoo/bin/foomoduleFooclassCLI但是当我输入时这不起作用:foocounter5moduleFooclassCLI','Countupfromtheinput.')endclassCounter:numeric,:desc=>"Thenumbertostartcounting"desc"Prints2numbersb
我的项目中有一个类别和子类别模型。我想以灵活的方式拥有许多子级别。我想制作一个self引用的“父”外键,但我不太确定该怎么做。有任何想法吗?谢谢!Cat1Sub1SubSub1SubSub2Sub2Cat2Sub1Cat3Sub1Sub2SubSub1 最佳答案 试试acts_as_tree插件 关于ruby-on-rails-在Rails中实现具有灵活深度的类别和子类别的最佳方法?,我们在StackOverflow上找到一个类似的问题: https://st
我是Ruby新手,在Ubuntu12.04机器上安装了Ruby1.9.3。每当我在终端中不带任何参数地运行ruby命令时,它就会挂起并且什么都不做。它在我的WindowsXP安装上做同样的事情。这是预期的行为吗?来自Python/Java背景,我期待某种输出。 最佳答案 这是预料之中的。当您只运行ruby时,它会停在那里,等待来自STDIN的程序,后跟一个文件结束符,然后执行该程序。如果你想要交互,比如当你运行python时,你需要Ruby的irb。 关于ruby-使用不带参数的'rub
我觉得我应该先发制人地道歉,因为这似乎是以前可能被问过的问题类型。我找不到答案,所以我在这里问。我正在查看RubyKoans,我在about_strings.rb的第24行有一个测试:deftest_use_flexible_quoting_to_handle_really_hard_casesa=%(flexiblequotescanhandleboth'and"characters)b=%!flexiblequotescanhandleboth'and"characters!c=%{flexiblequotescanhandleboth'and"characters}assert_
我有一个表示为字符串“01100011....”的二进制数(52位)计算1的个数最快的方法是什么?"01100011....".count("1")显然有效,但如果此操作需要执行数千次,则非常耗时。好的,还有更多信息。我正在尝试为单词创建位向量,如下所示defbit_vec(str)alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZ'bv=""alphabet.each_chardo|a|ifstr.include?(a)bv+="1"elsebv+="0"endendbvendbit_vec方法被调用了大约17万次。我将位向量存储在哈希中,并使用它们通过对位向量
我正在尝试根据给定年份的周数进行一些日期计算。例如:date=Date.today#Monday,March5,2012putsdate.cwyear#2012putsdate.cweek#10(10thweekof2012)既然我知道当前是几号,我想弄清楚下一周和上一周是什么。我需要获取年份(2012)和周数(10)并将其转回日期对象,以便计算下一周/上一周的值。我该怎么做? 最佳答案 你想要Date.commercial:require'date'now=Date.today#=>2012-03-05monday_next_we
Ruby正确解析第一个日期,但第二个日期不正确。使用ruby1.9.3和2.1.2测试。知道如何让它始终如一地工作吗?(我们将出生日期设为2位数年份)Date.strptime("10/11/89","%d/%m/%y")=>Fri,10Nov1989Date.strptime("15/10/63","%d/%m/%y")=>Mon,15Oct2063 最佳答案 strptime方法将文本“63”解析为2063年,而不是您想要的1963年。这是因为该方法使用POSIXstandard来决定世纪。.chronicgem也有类似的问
我正在尝试在Ruby中编写一个正则表达式来搜索字符串中只有四位数字的数字。我正在使用/\d{4}/但这是给我四位数或更多位数的数字。例如:“12345-456-6575一些文本9897”在这种情况下,我只需要9897和6575,但我还得到了长度为五个字符的1234。 最佳答案 "12345-456-6575sometext9897".scan(/\b\d{4}\b/)=>["6575","9897"] 关于ruby-如何编写正则表达式以仅查找四位数的数字?,我们在StackOverflo
我正在尝试制作一个邮政编码数组。array=[07001,07920]返回:array=[07001,07920]^from(irb):12from:0以前从来没有见过这个。有什么解决方法吗? 最佳答案 Ruby将前导0的数字解释为八进制(基数8)。因此数字8和9无效。将邮政编码存储为字符串而不是数字可能更有意义(以避免在显示时必须用零填充),例如:array=["07001","07920"] 关于ruby-on-rails-什么是非法八进制数字?,我们在StackOverflow上找