目 录

  • 三、猜字母
  • 六、奇怪的分式
  • 七、扑克排序
  • 八、分糖果
  • 九、地宫取宝


三、猜字母

把abcd…s共19个字母组成的序列重复拼接106次,得到长度为2014的串。

接下来删除第1个字母(即开头的字母a),以及第3个,第5个等所有奇数位置的字母。

得到的新串再进行删除奇数位置字母的动作。如此下去,最后只剩下一个字母,请写出该字母。

【解析】:模拟

构建StringBuilder并初始化
注意每删除一个数下标会发生变化
找到规律后写出循环

StringBuilder sb=new StringBuilder();
for(int i=1;i<=106;i++)
	for(int j=97;j<116;j++)
		sb.append((char)j);

while(true) {
	if(sb.length()==1){
		System.out.println(sb.charAt(sb.length()-1));
		break;
	}
	for(int i=0;i<sb.length();i++) {
		sb.deleteCharAt(i);
	}
}

六、奇怪的分式

上小学的时候,小明经常自己发明新算法。一次,老师出的题目是: 1/4 乘以 8/5 小明居然把分子拼接在一起,分母拼接在一起,答案是:18/45

java 避坑 java b_递归


老师刚想批评他,转念一想,这个答案凑巧也对啊,真是见鬼!


对于分子、分母都是 1~9 中的一位数的情况,还有哪些算式可以这样计算呢?



请写出所有不同算式的个数(包括题中举例的)。



显然,交换分子分母后,例如:4/1 乘以 5/8 是满足要求的,这算做不同的算式。



但对于分子分母相同的情况,2/2 乘以 3/3 这样的类型太多了,不在计数之列!

【解析】:枚举、最大公约数
判断两个分式是否相等,只需判断约分后的值是否相等
暴力枚举所有情况,判断约分后是否相等

public static void main(String[] args) {
	Scanner sc = new Scanner(System.in);
	int ans=0;
	//暴力枚举所有情况
	for(int a=1;a<10;a++) {
		for(int b=1;b<10;b++) {
			if(a!=b)
			for(int c=1;c<10;c++) {
				for(int d=1;d<10;d++) {
					//判断约分后是否相等
					if(c!=d&&Arrays.equals(f(a*c,b*d), f(a*10+c,b*10+d)))
						ans++;
				}}}}
	System.out.println(ans);
}

//约分
static int[] f(int a, int b) {
	int[] x = new int[2];
	for(int i=a;i>1;i--) {
		if(b%i==0&&a%i==0) {
			x[0]=a/i;
			x[1]=b/i;
			return x;
		}	
	}
	x[0]=a;
	x[1]=b;
	return x;
}

七、扑克排序

A A 2 2 3 3 4 4, 一共4对扑克牌。请你把它们排成一行。

要求:两个A中间有1张牌,两个2之间有2张牌,两个3之间有3张牌,两个4之间有4张牌。
请填写出所有符合要求的排列中,字典序最小的那个。

例如:22AA3344 比 A2A23344 字典序小。当然,它们都不是满足要求的答案。

【解析】:全排列
全排列扑克牌,再判断

  • 两个A中间有1张牌 -> 两个A下标差为2
  • 如何区分重复 -> s.lastIndexOf(‘A’) - s.indexOf(‘A’)
  • 如何去重复 -> 利用set集合不能有重复的特性
static char[] arr= {'A','A','2','2','3','3','4','4'};
static Set<String> set=new HashSet<>();				//去重
public static void main(String[] args) {
	Scanner sc = new Scanner(System.in);
	f(0);
}

//全排列
static void f(int k) {
	if(k==arr.length&&check()) {
		set.add(Arrays.toString(arr));
	}
	for(int i=k;i<arr.length;i++) {
		char t=arr[k];
		arr[k]=arr[i];
		arr[i]=t;
		f(k+1);
		t=arr[k];
		arr[k]=arr[i];
		arr[i]=t;
	}
}

//检查是否满足条件
static boolean check() {
	String s=new String(arr);
	if(s.lastIndexOf('A') - s.indexOf('A') == 2 && s.lastIndexOf('2') - s.indexOf('2') == 3
			 && s.lastIndexOf('3') - s.indexOf('3') == 4 && s.lastIndexOf('4') - s.indexOf('4') == 5)
		return true;
	return false;
}

八、分糖果

有n个小朋友围坐成一圈。老师给每个小朋友随机发偶数个糖果,然后进行下面的游戏:

每个小朋友都把自己的糖果分一半给左手边的孩子。

一轮分糖后,拥有奇数颗糖的孩子由老师补给1个糖果,从而变成偶数。

反复进行这个游戏,直到所有小朋友的糖果数都相同为止。

你的任务是预测在已知的初始糖果情形下,老师一共需要补发多少个糖果。

【格式要求】
程序首先读入一个整数N(2<N<100),表示小朋友的人数。
接着是一行用空格分开的N个偶数(每个偶数不大于1000,不小于2)
要求程序输出一个整数,表示老师需要补发的糖果数。

3
2 2 4

4

【解析】:模拟、枚举

数组的操作,
每轮按要求更改小朋友的糖果数
糖果数为奇数,则补一个,答案++
当所有小朋友糖果数相等则停止循环

注意:所有小朋友是同时分糖果
所以需要一个变量暂存第一个的初始值

public static void main(String[] args) {

		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		int[] arr = new int[n];
		for (int i = 0; i < arr.length; i++)
			arr[i] = scan.nextInt();
		int ans = 0;

		while (true) {
			int t = arr[0];
			for (int i = 0; i < n - 1; i++) {
				arr[i] = arr[i] / 2 + arr[i + 1] / 2;
				if ((arr[i] & 1) == 1) {
					ans++;
					arr[i]++;
				}
			}
			arr[n - 1] = arr[n - 1] / 2 + t / 2;
			if ((arr[n-1] & 1) == 1) {
				ans++;
				arr[n-1]++;
			}
			if (check(arr, n)) {
				System.out.print(ans);
				return;
			}
		}
}

static boolean check(int[] a, int n) {
	int t = a[0];
	for (int i = 1; i < n; i++)
		if (a[i] != t)
			return false;
	return true;
}

九、地宫取宝

X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。

小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。

当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。

请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。

【数据格式】
输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)
接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值
要求输出一个整数,表示正好取k个宝贝的行动方案数。该数字可能很大,输出它对 1000000007 取模的结果。

2 3 2
1 2 3
2 1 5

14

*【60分解析】:递归

第一步:递归参数(选择物品后什么会发生变化)
位置移动 -> x、y 坐标
宝贝价值比 小明手中任意宝贝价值 都大 -> 最大值 max
当前手中有几件宝贝 -> cnt

第二步:限制条件
下标越界 -> 回溯
走到终点 -> 判断是否满足要求(正好取k个宝贝)
注意终点格子的宝贝也要判断是否可取( cnt==k-1 && cur>max )

第三步:主体
每个节点有四种走法
拿物品向右或向下,
不拿物品向右或向下

static int[][] map=new int[50][50];
static int n,m,k;
static long ans;
static final long MOD=1000000007;

public static void main(String[] args) {
	Scanner scan = new Scanner(System.in);
	n=scan.nextInt();
	m=scan.nextInt();
	k=scan.nextInt();
	for(int i=0;i<n;i++) {
		for(int j=0;j<m;j++) {
			map[i][j]=scan.nextInt();
		}
	}
	dfs(0,0,-1,0);//第一个点价值可能为0
	System.out.println(ans);
}

static void dfs(int x, int y, int max, int cnt) {
	if(x==n||y==m)
		return;
	int cur=map[x][y];
	if(x==n-1&&y==m-1) {
		if(cnt==k||(cnt==k-1&&cur>max)) {//最后一个格子可能取
			ans++;
			if(ans>MOD) ans%=MOD;
		}
	}
	
	if(cur>max) {						//可以取这个物品
		dfs(x+1,y,cur,cnt+1);
		dfs(x,y+1,cur,cnt+1);
	}
	//对于价值小,或者价值大但不取这个物品
	dfs(x+1,y,max,cnt);
	dfs(x,y+1,max,cnt);
}

【满分解析】:记忆化搜索、动态规划
使用记忆数组用 递归参数 作为下标储存 递归结果,
其余部分参考上面

static int n,m,k;
static int[][] val=new int[51][51];
static long[][][][] dp=new long[51][51][15][15];

public static void main(String[] args) {
	// TODO Auto-generated method stub
	Scanner sc=new Scanner(System.in);
	n=sc.nextInt();
	m=sc.nextInt();
	k=sc.nextInt();
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			val[i][j]=sc.nextInt();
		}
	}
	
       for(int i=0;i<51;i++)
       	for(int j=0;j<51;j++)
       		for(int k=0;k<15;k++)
       			for(int l=0;l<15;l++)
       				dp[i][j][k][l]=-1;		//初始化为-1
	
	System.out.println(dfs(0,0,-1,0));
}

static long dfs(int x,int y,int max,int cnt){
	if(dp[x][y][max+1][cnt]!=-1)			//传入的是-1下标越界,所以实际存取的时候+1
		return dp[x][y][max+1][cnt];
	
	if(x==n||y==m||cnt>k)
		return 0;
	int cur=val[x][y];
	if(x==n-1&&y==m-1){
		if(cnt==k||(cnt==k-1&&cur>max))
			return 1;
		return 0;
	}

	long ans=0;								//要保存在记忆数组的值
	if(cur>max){
		ans+=dfs(x+1,y,cur,cnt+1);
		ans+=dfs(x,y+1,cur,cnt+1);
	}
	
	ans+=dfs(x+1,y,max,cnt);
	ans+=dfs(x,y+1,max,cnt);

	return dp[x][y][max+1][cnt]=ans%1000000007;
}