枚举(一)
枚举就是根据提出的问题,列出该问题的所以可能的解,并在逐一列出的过程中,检验每个可能解是否是问题真正的解,如果是,就采纳这个解,如果不是就继续判断下一个。
枚举法一般比较直观,容易理解,但由于要检查所有可能的解,因此运行效率较低
能用枚举法解决的问题一般具有以下特征:
①解枚举范围是有穷的
②检验条件是确定的
枚举法的结构是:枚举范围循环+条件判断语句
例题1:某君说我的年龄是两位数,我比我的儿子大27岁,如果把我年龄的两位数交换位置,刚好就是我儿子的年龄
请你计算 某君的年龄一共有多少种可能情况?
分析:
①年龄是两位数,则取值范围是[10,99]内的整数(解枚举范围是有穷的)
②把枚举年龄的个位数与十位数交换,如果发现刚好比原数字小27,那么它就是真正的解(检验条件是确定的)
则可以用枚举法
public class Main {
public static void main(String[] args) {
int total=0;
for(int i=10;i<100;i++){
int a=i%10;//计算出年龄的个位数
int b=i/10;//计算出年龄的十位数
if(i==a*10+b+27)
{
total++;
}
}
System.out.println(total);
}
}
例题2:如果一个三位数,它的每个位上的数字的三次幂之和等于它本身,那么我们就称这个数字是一个水仙花数,现在求出所有的水仙花数。
分析:
①一个三位数,则取值范围是[100,999]内的整数(解枚举范围是有穷的)
②每个位上的数字的三次幂之和等于它本身(检验条件是确定的)
则可以用枚举法
public class Main {
public static void main(String[] args) {
for(int i=100;i<1000;i++)
{
int a=i/100;//百位
int x=i%100;
int b=x/10;//十位
int c=x%10;//个位
if(i==Math.pow(a,3)+Math.pow(b,3)+Math.pow(c,3))
{
System.out.println(i);
}
}
}
}
枚举(二)之前的枚举都是只枚举了一个变量的解,往往枚举的题目需要枚举很多变量,条件判断也会有很多繁琐的情况
例题3:
口+口=口
口-口=口
口x口=口
口/口=口 每个方块代表1-13中的数字,但不能重复,交换加法 乘法前后的操作数后算不同的方案,算一算,一共有多少种方案
public class Main {
public static void main(String[] args) {
int a1,a2,a3,a4,a5,a6,a7,a8;
int[] vis;
vis=new int[14];
int tot=0;//记录合法的方案数
for(a1=1;a1<=13;++a1)
{
vis[a1]=1;
for(a2=1;a2<=13;++a2)
{
if(a1+a2>13||vis[a2]==1)
{
continue;
}
vis[a2]=1;
vis[a1+a2]=1;
for(a3=1;a3<=13;++a3)
{
if(vis[a3]==1)
{
continue;
}
vis[a3]=1;
for(a4=1;a4<=13;++a4)
{
if(a3-a4<1||a3-a4>13||vis[a4]==1||vis[a3-a4]==1||a4==a3-a4)
{
continue;
}
vis[a4]=1;
vis[a3-a4]=1;
for(a5=1;a5<=13;++a5)
{
if(vis[a5]==1)
{
continue;
}
vis[a5]=1;
for(a6=1;a6<=13;++a6)
{
if(a5*a6>13||vis[a6]==1||vis[a5*a6]==1||a6==a5*a6)
//注意先判断a5*a6的大小,如果先判断vis[a5*a6]==1则可能发生溢出,||表示如果一个条件是错误的,则其余的不会再判断
{
continue;
}
vis[a6]=1;
vis[a5*a6]=1;
for(a7=1;a7<=13;a7++)
{
if(vis[a7]==1)
{
continue;
}
vis[a7]=1;
for(a8=1;a8<=13;a8++)
{
if(vis[a8]==1||a7<a8||a7%a8!=0||vis[a7/a8]==1||a8==a7/a8)
{
continue;
}
tot++;
}
vis[a7]=0;
}
vis[a6]=0;
vis[a5*a6]=0;
}
vis[a5]=0;
}
vis[a4]=0;
vis[a3-a4]=0;
}
vis[a3]=0;
}
vis[a2]=0;
vis[a1+a2]=0;
}
vis[a1]=0;
}
System.out.println(tot);//输入 64
}
}
例题:口口口+口口口=口口口 为1-9中的数字,不能重复,如173+286=459是正确的,请问一共有多少种合理的组合?
173+286=459 和286+173=459相同
public class Main {
public static void main(String[] args) {
int a1,a2,a3,a4,a5,a6,a7,a8,a9;
int[] vis=new int[10];
int tot = 0;
for(a1=1;a1<=9;a1++)
{
vis[a1]=1;
for(a2=1;a2<=9;a2++)
{
if(vis[a2]==1)
{
continue;
}
vis[a2]=1;
for(a3=1;a3<=9;a3++)
{
if(vis[a3]==1)
{
continue;
}
vis[a3]=1;
for(a4=1;a4<=9;a4++)
{
if(vis[a4]==1)
{
continue;
}
vis[a4]=1;
for(a5=1;a5<=9;a5++)
{
if(vis[a5]==1)
{
continue;
}
vis[a5]=1;
for(a6=1;a6<=9;a6++)
{
if(vis[a6]==1)
{
continue;
}
vis[a6]=1;
for(a7=1;a7<=9;a7++)
{
if(vis[a7]==1)
{
continue;
}
vis[a7]=1;
for(a8=1;a8<=9;a8++)
{
if(vis[a8]==1)
{
continue;
}
vis[a8]=1;
for(a9=1;a9<=9;a9++)
{
if(vis[a9]==1)
{
continue;
}
if((a1*100+a2*10+a3)+(a4*100+a5*10+a6)==(a7*100+a8*10+a9))
{
tot++;
}
}
vis[a8]=0;
}
vis[a7]=0;
}
vis[a6]=0;
}
vis[a5]=0;
}
vis[a4]=0;
}
vis[a3]=0;
}
vis[a2]=0;
}
vis[a1]=0;
}
System.out.print(tot/2);
}
}
小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了。
有一次,老师出的题目是:36 x 495 = ?
他却给抄成了:396 x 45 = ?
但结果却很戏剧性,他的答案竟然是对的!!
因为 36 * 495 = 396 * 45 = 17820
类似这样的巧合情况可能还有很多,比如:27 * 594 = 297 * 54
假设 a b c d e 代表1~9不同的5个数字(注意是各不相同的数字,且不含0)
能满足形如: ab * cde = adb * ce 这样的算式一共有多少种呢?
请你利用计算机的优势寻找所有的可能,并回答不同算式的种类数。
满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。
答案直接通过浏览器提交。
注意:只提交一个表示最终统计种类数的数字,不要提交解答过程或其它多余的内容。
能满足形如:ab * cde = adb * ce
这样的算式一共有(5分)种
public class Main {
public static void main(String[] args) {
int a1,a2,a3,a4,a5;
int[] vis=new int[11];
int tot = 0;
for(a1=1;a1<=9;a1++)
{
vis[a1]=1;
for(a2=1;a2<=9;a2++)
{
if(vis[a2]==1)
{
continue;
}
vis[a2]=1;
for(a3=1;a3<=9;a3++)
{
if(vis[a3]==1)
{
continue;
}
vis[a3]=1;
for(a4=1;a4<=9;a4++)
{
if(vis[a4]==1)
{
continue;
}
vis[a4]=1;
for(a5=1;a5<=9;a5++)
{
if(vis[a5]==1)
{
continue;
}
if((a1*10+a2)*(a3*100+a4*10+a5)==(a1*100+a4*10+a2)*(a3*10+a5))
{
tot++;
}
}
vis[a4]=0;
}
vis[a3]=0;
}
vis[a2]=0;
}
vis[a1]=0;
}
System.out.print(tot);
}
}
二进制枚举子集
给定一个集合,枚举所以可能的子集。可以用二进制法。用二进制的一位表示集合对应某一元素的选取状态,1表示选取,0表示不选
位运算
A&B 与 (全为1才是1)
A|B 或 (有一个1就是1)
A^B 异或 (两者同为0或1时才是0,其他为1)
位运算符中有两种操作,左移<<和右移>>
对于A<<B,表示A转换为二进制后,向左移动B位,(在末尾添加B个0)
对于A>>B,表示A转换为二进制后,向右移动B位,(删除末尾的B位)
比如2<<2,2转换为二进制为 01,10左移动2位,就变成了二进制1000,转换为十进制是8,即2<<2=8
例4:
诗人李白,一生好饮酒,逢店加一倍,遇花喝一斗。一共遇店10次,遇花5次,已知最后一次遇到的是花,他正好把酒喝光了,请你计算遇到花和店的次序,有多少种可能的方案。
解法:用二进制枚举,已知遇店10次,遇花5次,并且最后一次遇到的花,正好把酒喝完,把遇到店作为二进制中的1,遇到花作为二进制中的0,已知最后一次遇到的是花,所以我们判断枚举的结果是否刚好有5个1和9个0,那我们就枚举出14位二进制的所有可能并加以判断,判断思路为是否有5个1和9个0,并且最终酒刚好剩一斗。