一、课堂重点内容:
1、枚举:个人见解为枚举就是一种知到一些必要的条件,然后从所有的数中选出一些符合条件的特殊的数字,比如我i们想要计算的是从1到10000之间的整数的和,我们就需要使用枚举的方法先将这些数字枚举出来,再进行计算,而在这个枚举的过程中,我们需要用到的方法是更具条件建立循环。再JAVA中常见的循环方式有三种,分别是while、for还有do while,其中的前两种更为常见
*while、do while和for的区分:若明确循环次数还有循环的条件,那么就使用for;
若不明确循环次数和循环条件,就需要使用do while或while
举个栗子,如lanqiaoOJ中的613题:
一个整数如果只含有因子3、5、7,称为幸运数字。 前10个幸运数字是3、5、7、9、15、21、25、27、35、45。 问59084709587505 是第几个幸运数字。
首先对题目进行分析:
循环条件:整数且只含有3、5、7这几个因子
循环次数:59084709587505
所以本题就是一个可以使用for循环来写的题目,具体的代码如下:
public class recview2023_3_16 {
public static void main(String args[])
{
long n=59084709587505L;//这是一个很大的数字,使用int无法完全表达,所以就需要使用一个比int还大的整形变量“long”
//需要注意的是,long的最后需要加上一个‘L’或是‘l(小写的L)’
int count=0;//定义一个整形变量count用于记录
//因为这个数字的因数是3、5、7,所以这个数字写成是3^i*5^j*7^k
for(int i=0;Math.pow(3, i)<n;i++)//3^i的最大值需要小于59084709587505
{
for(int j=0;Math.pow(5, j)<n;j++)
{
for(int k=0;Math.pow(7, k)<n;k++)
{
if(Math.pow(3, i)*Math.pow(5, j)*Math.pow(7, k)<n)//3、5、7的i、j、k次相乘小于59084709587505,那么就还不达到要求,需要继续循环
count++;//在数值未达到要求前的每一个结果都符合整数如果只含有因子3、5、7,所以记录一次
}
}
}
System.out.println(count);//输出放在三个for循环外面,只有三个数的乘积大于59084709587505时才会输出,而输出的count就代表着59084709587505是第几个符合要求的数字
}
}
2、组合
组合是一个很奇妙的东西,简单的来说就是在n个数字中,随机的选出m个,然后我们需要算出一共有多少种方法,上课的时候学长给了一个固定的模板,可以直接记忆,代码和解析如下:
static int m,n,ans;//先在整个Class里面定义m,n,ans三个整数,因为这三个整数通篇都可以用到,所以就可以使用static int的方法直接定义
static Vector<Integer> chosen=new Vector<Integer>();//定义一个Vector数组
public static void check(int x)//定义一个检查条件,用于去除不符合条件的数
{
if(chosen.size()>m||chosen.size()+(n-x+1)<m)//如果选择的数超出m或是剩下的数已经不够m个
return;//那就直接返回
if(x==n+1)//如果m的个数符合
{
String str="";
for(int i=0;i<chosen.size();i++)
System.out.print(chosen.get(i)+" ");//把当前的数组输出
System.out.println("");
ans++;//每输出一个数组就记录一次,表示这是第n个数组
return;//返回继续判断
}
check(x+1);//判断x的下一个
chosen.addElement(x);//将指定的分量添加到此向量的末尾,将其大小增加 1。如果此向量的大小大于其容量,则其容量将增加。
check(x+1);//判断上一步中的结果
chosen.remove((Object)x);//删除此 Vector 中指定元素的第一个匹配项如果 Vector 不包含该元素,则它保持不变。也可以认为是删除具有最低索引i的元素,以便Objects.equals(o,get(i))(如果存在这样的元素)。
}
public static void main(String args[])
{
Scanner sc=new Scanner(System.in);//输入语句
n=sc.nextInt();//输入样本总数
m=sc.nextInt();//从中挑选m个
check(1);//从1开始
System.out.println(ans);//输出统计结果
}
3、排列
排列枚举和组合枚举大体上相似,但是排列枚举多了一些限制,先展示代码,解析会在后面另行补充
static int n;
static int[] order =new int[20];
static boolean[] chosen =new boolean[20];
static <object> void calc(int x) {
if (x == n + 1) { //选够了m个数输出
String ansTem = "";
for (int i = 1; i <=n ; i++)
System.out.println(order[i]);
return;
}
for (int i = 1; i <= n; i++) {
if (chosen[i]) continue;
order[x] = i;
chosen[i] =true;
calc(x + 1);
chosen[i] = false;
order[x] = 0;
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
n = in.nextInt();
for (int i = 0; i < n; i++) {
String s;
s=in.next();
name.addElement(s);
}
calc(1);
}
二、课后练习题
1、约数个数
1200000 有多少个约数(只计算正约数)。
这是一个很简单的暴力枚举题,代码及解析如下:
public class Main
{
public static void main(String args[])
{
int ans=0,n=1200000;//定义一个ans用于记录,目标数值是1200000
for(int i=1;i<=n;i++)//一个数的约数一定小于等于这个数的本身,所以从1开始取余数,一直取到这个数本身。
{
if(n%i==0)//要是余数为0,就说明这个数是1200000的约数
ans++;//个数加1
}
System.out.println(ans);//输出个数的总数
}
}
2、特殊时间
本体的重点在于年月日加时间中所有的数字一共有两种,而且这两种数字出现的比例为3:1
代码与详细解析如下:
public class Main
{
static int P[]={0,31,28,31,30,31,30,31,31,30,31,30,31};//先把每个月的天数考虑出来,在本题中0229是一个违法数字,所以我们不用考虑闰年
static int checkD(int D)//先定义一个子函数用于检查日期的合法性
{
int month=D/100;//取出前两位是月份
int day=D%100;//取出后两位是日期
if(month<1||month>12) return 0;//非法,则返回0
if(day<1||day>P[month])return 0;
return 1;//合法,则返回1
}
static int checkH(int H)//检时间的合法性
{
int h=H/100;//取出前两位是小时
int m=H%100;//取出后两位是分钟
if(h<0||h>23)return 0;//非法,则返回0
if(m<0||m>59)return 0;
return 1; //合法,则返回1
}
public static void main(String args[])
{
int ans=0;//定义一个为0的整数用于记录
for(int a=0;a<=9;a++)//定义组成日期的a,b两个数,两个数的取值范围都属0——9
for(int b=0;b<=9;b++)
if(a!=b)//a和b不相等是前提
{
int A[]=new int[] {a,a,a,a};//先定义一个全都是a的数组
int NY=4,ND=0,NH=0;//分别定义合法排序的初始值,因为年份没有非法情况,所以可以直接定于为4(a,a,a,b)、(a,a,b,a)、(a,b,a,a)、(b,a,a,a,)
for (int i=0;i<4;i++)//每个小分组一共有4位数,所以把他们拆开在分别替换成b的值
{
A[i]=b;//把第i个数替换成b当前的数值
int number=0;
for(int j=0;j<4;j++)//把被拆散的四位数还原
number=number*10+A[j];
ND+=checkD(number);//判断当前的四位数是否是合法月份+日期
NH+=checkH(number);//判断当前的四位数是否是合法的时间
A[i]=a;//将替换成b的数还原为a
}
ans+=NY*ND*NH;//所有的种类等于三个部分的所有可能的乘积
}
System.out.println(ans);//输出该乘积,就是所有符合条件的合法日期
}
}
3、卡片
小蓝有很多数字卡片,每张卡片上都是数字 00 到 99。
小蓝准备用这些卡片来拼一些数,他想从 11 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。小蓝想知道自己能从 11 拼到多少。例如,当小蓝有 3030 张卡片,其中 00 到 99 各 33 张,则小蓝可以拼出 11 到 1010,但是拼 1111 时卡片 11 已经只有一张了,不够拼出 1111。现在小蓝手里有 00 到 99 的卡片各 20212021 张,共 2021020210 张,请问小蓝可以从 11 拼到多少?
这个题目看起来有些难度,但是仔细阅读就会发现这是一个消耗1的问题,把所有的1消耗完就不可以再拼出新的数字了。
简单的解决方法是直接借助word文档就可以
public class Main{
public static void main(String args[])
{
for (int i=0;i<20202;i++)
System.out.println(i);
}
}
直接使用java打赢一个较大的数,然后全选复制粘贴到word文档中,然后再找到第2021个1,其对应的数就是我们要找的数