带分数(全排列 + 枚举)

1. 问题描述:

100 可以表示为带分数的形式:100 = 3 + 69258 / 714
还可以表示为:100 = 82 + 3546 / 197
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。

 类似这样的带分数,100 有 11 种表示法。
题目要求:
从标准输入读入一个正整数N (N<1000*1000)
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!
例如:
用户输入:
100
程序输出:
11
再例如:
用户输入:
105
程序输出:
6
资源约定:
峰值内存消耗(含虚拟机) < 64M
CPU消耗  < 3000ms

2. 思路分析:

① 因为它是由不重复的1-9之间的数字构成的具有加号与除号的算术表达式, 所以首先我们需要知道1-9数字形成的全排列,这样才可以对全排列的数字序列进行加号成除号的插入操作的尝试

对于求解全排列的方法有很多种,其中比较简洁和方便的方法是使用递归的方式来进行求解,可以先声明一个数组,数组中元素为1-9之间的数字,然后我们可以在递归的方法中传入一个变量来记录当前的数字所在的位置,在一个for循环中我们可以尝试将当前位置上的数字与其他位置上的数字进行交换,所以在for循环中进行递归就可以形成所有的全排列

private static void solve(int[] arr, int k) {
	if(k == 9){
	    //尝试去插入符号
	    check(arr);
	    return;
    }
	for(int i = k; i < 9; i++){
		int t = arr[i];
		arr[i] = arr[k];
		arr[k] = t;
		solve(arr, k + 1);
        //回溯是为了形成有序的全排列
		t = arr[i];
		arr[i] = arr[k];
		arr[k] = t;
	}
}

② 在进入k == 9的判断之后说明已经形成了一个排列,这个时候我们需要对当前形成的这个排列进行加号和除号的插入,这个时候就需要使用到枚举了,因为要插入两个符号,所以对于一个排列来说我们可以最多在七个数字序列形成的整数之后插入一个加号,后面还有一个除号和两个相除的数字序列形成的整数,所以可以得到:

a:加号前面的数字最多具有七个

b:加号之后的数字也是最多有七个,剩下来的就是除号之后的数字了

这也即就是我们对形成的全排列划分出了三部分,所以可以使用两层循环来进行表示,第一个for循环表示的是从开始位置到能够加入数字序列的最多的那个位置,即第七个位置,所以变量i应该从0到6表示的就是最多有七个数字

第二层循环表示的是加号后面的数字序列,因为前面是i个字符,所以j应该从i + 1的位置开始计算,最多也是7个字符,这样j能够形成的最多的数字序列就是i + 1到第八个位置的数字形成的序列,剩下的就是除号后面形成的数字序列

③ 上面的for循环解决的是通过运算符划分数字的的区域,也就是符号前后的开始位置和结束位置形成的数字序列,因为数组中的元素是分散的,所以对于开始位置和结束位置需要将其所以数字处理之后形成一个整数,这样才可以进行对目标数字进行比对

我们可以使用一个方法来解决这个问题,在方法中传入数组,数字的开始位置和结束位置,使用一个变量来改变当前循环中的位上的权重

private static int toInt(int arr[], int begin, int end) {
	int t = 1;
	int sum = 0;
	for(int i = end; i >= begin; i--){
		sum += arr[i] * t;
		t *= 10;
	}
		return sum;
}

④ 需要注意取某一段的数字的时候需要注意边界的问题,第一段是0到i这个位置,第二段是i + 1到j这个位置,剩下来的就是j + 1到最后的位置

⑤ 可以在if判断满足条件的数字输出相应的结果来测试程序是否正确

3. 代码如下:

import java.util.Scanner;
public class Main {
	static int N;
	static int count = 0;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		N = sc.nextInt();
		int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
		solve(arr, 0);
		System.out.println(count);
		sc.close();
	}
	
	private static void solve(int[] arr, int k) {
		if(k == 9){
			//尝试去插入符号
			check(arr);
			return;
		}
		for(int i = k; i < 9; i++){
			int t = arr[i];
			arr[i] = arr[k];
			arr[k] = t;
			solve(arr, k + 1);
			t = arr[i];
			arr[i] = arr[k];
			arr[k] = t;
		}
	}

	private static void check(int[] arr) {
		for(int i = 0; i < 7; i++){
			int num1 = toInt(arr, 0, i);
			if(num1 >= N) continue;
			for(int j = i + 1; j < 8; j++){
				int num2 = toInt(arr, i + 1, j);
				int num3 = toInt(arr, j + 1, 8);
				if(num2 % num3 == 0 && num1 + num2 / num3 == N){
					count++;
				}
			}
		}
	}

	private static int toInt(int arr[], int begin, int end) {
		int t = 1;
		int sum = 0;
		for(int i = end; i >= begin; i--){
			sum += arr[i] * t;
			t *= 10;
		}
		return sum;
	}
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值