for循环枚举法,全排列+dfs,补充浮点数注意事项

  其实这个题目我一直没想好应该叫什么,就是在做蓝桥杯的时候会遇到很多的题,给你一等式,abcdef...分别是1-9(||12||15)不重复问你有几种方案?

  我之前一直都是用的for循环在做,听说这叫什么暴力破解还是枚举法的。小白不是很懂这些。

  但是之前做过一道题,好像就是15个数的for循环写的,一个是巨累(因为我用a!=b&&a!=c&&a!=....真的特别多而且容易写错),另一个是我记得我这道题写了很久最后提交没有分数,因为!!!超时了!!!

  真的特别气,当时的我只会做一种题就是for循环的这个,我觉得自己要凉凉了,唯一会的题还超时了。所以痛定思痛,自己去网上down了全排列+dfs的代码。

  恰巧这次的代码特别合适,我竟然看懂了(我以前也百度过,但是不记得为什么没有看懂,坚信我们for循环可以打天下的一直没有再去看),而且真的是要特别的夸一夸这一套代码,因为我之后所以的这种题,靠的都是这一套代码。特别省事,差不多可以算是我唯一的模板了。

  昨天的题有一道这样的,然后就想把我会的所有的方法整理出来,当然,模板万岁啊。但是如果有时间走两套程序的话,算是检查了,保证准确率。

 标题: 马虎的算式

小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了。

有一次,老师出的题目是: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 这样的算式一共有多少种呢?(满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。)

第一种方法:最简单的for循环法。最起码10以内很简单不会超时,只要仔细就不会出错

 1 public class Main {
 2
 3     public static void main(String[] args) {
 4         //2.能满足形如:ab * cde = adb * ce这样的算式一共有 ?种
 5                 int a,b,c,d,e;
 6                 int count=0;
 7                 for(a=1;a<10;a++){
 8                     for(b=1;b<10;b++){
 9                         for(c=1;c<10;c++){
10                             for(d=1;d<10;d++){
11                                 for(e=1;e<10;e++){
12                                     int x=(10*a+b)*(100*c+10*d+e);
13                                     int y=(100*a+10*d+b)*(10*c+e);
14                                     if(x==y&&
15                                         a!=b&&a!=c&&a!=d&&a!=e&&
16                                         b!=a&&b!=c&&b!=d&&b!=e&&
17                                         c!=a&&c!=b&&c!=d&&c!=e&&
18                                         d!=a&&d!=b&&d!=c&&d!=e&&
19                                         e!=a&&e!=b&&e!=c&&e!=d ){
20                                         count++;
21                                     }
22                                 }
23                             }
24                         }
25                     }
26                 }
27                 System.out.println(count);
28
29     }
30
31 }

ps:谁能想到,我第一次这个题写错了呢,因为我忽略了这句话,忘了判断是否是不同的数字了。--->假设 a b c d e 代表1~9不同的5个数字(注意是各不相同的数字,且不含0)在一个是,我看到题目中说--->满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。我做的时候没有判断是否不同数字,所以答案是1007,所以,我看到这句话,更加确定了,我做的真对啊。我以为他这句话是在提醒我,*2啊*2啊。结果当然是错的啦。他写这句话应该就是单纯的帮你减轻负担啊,不用考虑交换律的问题,这样出来多少就是多少了。

第二种方法:for循环法稍作改变。新建一个used数组,分别对应1 2 3 4.。。9然后用used数组的值标记是否使用过了。记得在这一轮的for循环最后释放哦。

 1 public class Main {
 2
 3     public static void main(String[] args) {
 4         //2.能满足形如:ab * cde = adb * ce这样的算式一共有 ?种
 5                 int a,b,c,d,e;
 6                 int count=0;
 7                 int []used=new int[11];
 8                 for(int i=1;i<10;i++){
 9                     used[i]=0;//0标记没有用过 1标记用过了
10                 }
11                 for(a=1;a<10;a++){
12                     used[a]=1;
13                     for(b=1;b<10;b++){
14                         if(used[b]==1)continue;
15                         used[b]=1;
16                         for(c=1;c<10;c++){
17                             if(used[c]==1)continue;
18                             used[c]=1;
19                             for(d=1;d<10;d++){
20                                 if(used[d]==1)continue;
21                                 used[d]=1;
22                                 for(e=1;e<10;e++){
23                                     if(used[e]==1)continue;
24                                     used[e]=1;
25                                     int x=(10*a+b)*(100*c+10*d+e);
26                                     int y=(100*a+10*d+b)*(10*c+e);
27                                     if(x==y){
28                                         count++;
29                                     }
30                                     used[e]=0;
31                                 }
32                                 used[d]=0;
33                             }
34                             used[c]=0;
35                         }
36                         used[b]=0;
37                     }
38                     used[a]=0;
39                 }
40                 System.out.println(count);
41
42     }

这里再备注一下,其实used直接建Boolean比较好,用false true,这样if判断的时候 就不用特意去写了。我这个是因为 我一开始用的boolean 运行的时候有错误,当时做的很烦躁就直接改了int 。之后在别的里面试了boolean完全可以没有问题,可能是其他的地方不知道那里报错了。

第三种方法:全排列+dfs

我其实一开始忘了这个办法,然后翻之前的笔记找到的。先写一下笔记上的原题:

A+B/C+DEF/GHI=0,A-I为1-9不重复,求有几种可满足式子。(DEF=D*100+E*10+F...)

 1 public class Main {
 2     static int count=0;
 3     public static void main(String[] args) {
 4         // TODO Auto-generated method stub
 5         float a[]=new float[10];
 6         boolean visit[]=new boolean[10];
 7         dfs(a,visit,1);
 8         System.out.println(count);
 9     }
10     private static void dfs(float[]a,boolean[]visit,int num){
11         if(num==10){
12             if(judge(a)){
13                 count++;
14             }
15             return;
16         }
17         for(a[num]=1;a[num]<10;a[num]++){
18             if(visit[(int)a[num]]==false){
19                 visit[(int)a[num]]=true;
20                 num=num+1;
21                 dfs(a,visit,num);
22                 num=num-1;
23                 visit[(int)a[num]]=false;
24             }
25         }
26     }
27     private static boolean judge(float[]a){
28         float A=a[1];
29         float B=a[2]/a[3];
30         float C=a[4]*100+a[5]*10+a[6];
31         float D=a[7]*100+a[8]*10+a[9];
32         if(A+B+C/D==10){
33             return true;
34         }
35         return false;
36     }
37
38 }

然后根据这个写了这道马虎的算式的代码:

 1 public class Main {
 2     //2.能满足形如:ab * cde = adb * ce这样的算式一共有 ?种
 3     static int count=0;
 4     public static void main(String[] args) {
 5         // TODO Auto-generated method stub
 6         float a[]=new float[6];//数组长度由题目中的变量个数决定abcde 5
 7         boolean visit[]=new boolean[10];
 8         dfs(a,visit,1);
 9         System.out.println(count);
10     }
11     private static void dfs(float[]a,boolean[]visit,int num){
12         if(num==6){//==前面数组长度
13             if(judge(a)){
14                 count++;
15             }
16             return;
17         }
18         for(a[num]=1;a[num]<10;a[num]++){
19             if(visit[(int)a[num]]==false){
20                 visit[(int)a[num]]=true;
21                 num=num+1;
22                 dfs(a,visit,num);
23                 num=num-1;
24                 visit[(int)a[num]]=false;
25             }
26         }
27     }
28     private static boolean judge(float[]a){
29         //这一块根据题目自己写
30         //1 2 3 4 5
31         //a b c d e
32         float A=a[1]*10+a[2];
33         float B=a[3]*100+a[4]*10+a[5];
34         float C=a[1]*100+a[4]*10+a[2];
35         float D=a[3]*10+a[5];
36         if(A*B==C*D){
37             return true;
38         }
39         return false;
40     }
41
42 }

这个全排列真的挺好用的,而且记住这个模板在做题特别省时间!!而且可以说完全不用过脑子了。

因为为了统一,a[]我就延续用了float,因为这种题真的就是来来回回的*||/所以 用float更放心一些。

补充:

1.拿浮点数进行暴力破解很危险啊,因为浮点数计算有误差!!!

double a =1.0;

double b=0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1;

输出a,b;得到的值是 a->1.0  b->0.999999999999

所以!如果sysout(a==b);必然是false。

2.那么怎么去比较呢?

  1.比较a-b是否小于一个比较小的数

System.out.println(Math.abs(a-b)<1e-6);

  2.避开浮点数,等式两边同时扩大。

  eg:求a*2.3+b*1.9==82.3可以转化成a*23+b*19==823

3.举例:浮点数的运算可以通过变换形式进行

???1/4=0   ==> int/int=int 所以0.25=>(int)0.25=>0

所以,1/a+1/b+1/c=1循环求解的时候可以

  1.  1.0/a+1.0/b+1.0/c=1.0    =>这个方法就避免了上面说的int/int =int

  2.     bc+ac+ab=abc  =>这样一转换,就和浮点数没有关系了,就不会影响了

4.举例:特殊值举例

1 System.out.println(3/0);
2 System.out.println(3.0/0);

执行第一行,输出:0不能为分母的英文,报错

执行第二行,输出:Infinity,即无穷大。

why???因为第二行 =>3.0是浮点数(double/float),所以0=>看作了一个0.00000000000....就是一个很很很小的数。3.0/一个很很很小的数===>无穷大

1      double a=3.0/0;
2         System.out.println(a+1);
3         System.out.println(a+a);
4         System.out.println(1/a);
5         System.out.println(1/(-a));
6         System.out.println(a-a);   

输出为:

Infinity
Infinity
0.0
-0.0
NaN

NaN=>not a number,不是一个数值。类似于小学说的无意义。当然NaN+||-一个数也都=NaN

5.任意精度问题(当float 不够->double,double不够?)

举例:有效数字100位(如1/6)

1     BigDecimal a=BigDecimal.valueOf(1).divide(BigDecimal.valueOf(6),new MathContext(100));
2        System.out.println(a);

输出:0.1666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667

正好做到这里插一个黄金连分数的题当例题了。

 1 import java.math.BigDecimal;
 2 import java.math.MathContext;
 3
 4 public class Main {
 5     public static void main(String[] args) {
 6         // TODO Auto-generated method stub
 7         BigDecimal bd = new BigDecimal(1);
 8         for (int i = 0; i < 10000; i++) {
 9             bd = bd.add(BigDecimal.ONE);
10 //          bd = BigDecimal.ONE.divide(bd, 100, BigDecimal.ROUND_HALF_DOWN);
11             bd = BigDecimal.ONE.divide(bd, new MathContext(100));
12         }
13         System.out.println(bd.toString());
14     }
15 }

输出:0.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911375

10 11两行都可以用,答案都是对的。

嗯嗯,终于完成今天的任务了。本来没想写关于浮点数的这些的,没想到写着写着就这么多了。啊,马上10点了,溜了溜了。

原文地址:https://www.cnblogs.com/ShallByeBye/p/8414523.html

时间: 2024-10-12 07:45:46

for循环枚举法,全排列+dfs,补充浮点数注意事项的相关文章

C++ 全排列问题——递归枚举法

全排列问题是一道非常经典的递归题目,而递归枚举法求解也是最暴力的一种方法. 例题 洛谷1706 题目描述 输出自然数1到n所有不重复的排列,即n的全排列,要求所产生的任一数字序列中不允许出现重复的数字. 输入格式 一个整数n. 输出格式 由1-n组成的所有不重复的数字序列,每行一个序列. 每个数字保留 5个场宽. 输入样例 3 输出样例 1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1 全排列问题--递归枚举法 这是一道经典的递归的题,每次递归枚举第x个数字是几,就是从1到

枚举:全排列

*/--> pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;} pre.src {background-color: Black; color: White;}

八皇后问题(回溯法&amp;枚举法)

作者 : 卿笃军 本文讨论了八皇后问题的三种解决方案: 一.枚举法 二.回溯法(递归版) 三.回溯法(非递归版) 本来这些代码是以前编写好的,没有发表,由于最近又学习到了八皇后问题,自己整理了一下发表了出来! 首先.说明一下何为八皇后问题,我也不去谷歌了,直接简单的说明一下: 八皇后问题,就是在一个8*8的平面棋盘上,要求你摆放8个棋子,要求:这8个棋子不能有2个在同一行,也不能有2个在同一列,同时一条斜线上面也不能有2个~~~~ 比如:4*4的棋盘,你可以这样摆放(4皇后问题): 以上图为参照

【算法学习笔记】28.枚举法 解题报告 SJTU_OJ 1255 1256 魔戒

1256. 你的魔戒?不,是你的魔戒.加强版 Description 在前往末日火山的途中,佛罗多与他的霍比特人同胞不幸被半兽人抓住了.半兽人要对每个霍比特人进行询问,以找出哪个霍比特人携带了至尊魔戒.每个霍比特人可能会说以下几种话: I have the ring. 我有魔戒. I have not the ring. 我没有魔戒. XXX has the ring. XXX有魔戒.(XXX表示某个霍比特人的名字) XXX has not the ring. XXX没有魔戒. Today is

枚举法的简单应用

箱子里有红,黄,蓝,白,黑五种颜色玻璃球若干,每次任意拿出三个球,问拿出三种不同颜色玻璃球的可能取法,请输出每种排列的情况. 注解:利用枚举法定义五种情况,“enum color {red,yellow,blue,white,black};”,定义四个整形变量“int i,j,k,m=0;”,依次从i到k分别进行从红到黑的情况循环,利用if来决断i,j,k互不相等的情况,针对每种情况通过调用函数来进行输出转换,得到结果. 编程: #include <iostream>using namespa

暴力枚举法总结

集训快要结束了,按照要求需要写一篇关于枚举的总结,于是在网上也看了许多其他菊苣写的文章,深受启发,但是思来想去感觉又不太系统,于是希望能在吸收那些知识后做一些整理,帮助后面的新人. 枚举的基本方法: 枚举,枚举,顾名思义,就是将所有情况都举出,并判断其是否符合题目条件.所以枚举的基本方法便是分析题意后,找到一个合适的维度列举每一个元素,以完成题目.其中如何找到一个合适的维度来进行枚举便是其中的最大难点. 枚举的基本条件: 首先是时间条件.一般来说主流的OJ当中,1000ms的时间限制下可以运行操

基础算法之二——枚举法

基础算法之二--枚举法"赛利的硬币" 题目描述 赛利有 12枚银币.其中有 11枚真币和1枚假币.假币看起来和真币没有区别,但是重量不同.但赛利不知道假币比真币轻还是重.于是他向朋友借了一架天平.朋友希望赛利称三次就能找出假币并且确定假币是轻是重.例如:如果赛利用天平称两枚硬币,发现天平平衡,说明两枚都是真的.如果赛利用一枚真币与另一枚银币比较,发现它比真币轻或重,说明它是假币.经过精心安排每次的称量,赛利保证在称三次后确定假币. 输入数据 输入有三行,每行表示一次称量的结果.赛利事先

枚举法判断某天是否为2015年节假日或周末

枚举法判断某天是否为2015年节假日或周末 1.节假日枚举类 /** *功能说明:节假日枚举类 * *创建人:Gansuper * *创建时间:2015-2-6 上午10:32:17 * *修改人 修改时间 修改描述 * */ public enum HolidayEnum { HOLIDAY(new String[]{"2015-01-01","2015-01-02","2015-01-03", "2015-02-18",&

【循环搜寻法(使用卫兵)】

/* 循环搜寻法(使用卫兵) 说明: 搜寻的目的,是在「已排序的资料」中寻找指定的资料,而当中循序搜寻是最基本的搜寻法,只要从资料开头寻找到最后,看看是 否找到资料即可. 解法: 初学者看到循序搜寻,多数都会使用以下的方式来进行搜寻: while(i < MAX){ if(number[i] == k){ printf("找到指定值"); break; } i++; } 这个方法基本上没有错,但是可以加以改善,可以利用设定卫兵的方式,省去if判断式,卫兵通常设定在数列最后或是最前