第四届蓝桥杯编程题:带分数 (转)

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

请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。

注意:主类的名字必须是:Main,否则按无效代码处理。

分析:这道题初看起来第一感觉就是用暴力破解应该可以搞定,但是时间复杂度应该会相当可观,仔细观察,会发现这道题无非是全排列的一种运用,把等式定义为:

N=A+B/C ,则ABC组合在一起就是1到9的一个全排列,所以可以把问题转换成对于一个9位数的数字,如何将其划分为A、B、C三部分,使得其满足N=A+B/C(隐含条件:

B%C==0),对于一个9位数可以这样考虑:A是不可能大于N的,所以A的位数只可能是从1位到和N相同位数这个范围,确定了A的位数之后,剩下的就是B和C的总位数,

B数字的开始位置即A数字的下一位,C数字的最后一位就是整个9位数的最后一位,那如何确定B的结束位置呢?

这里有个小技巧,可以大大减少可能性的判断:

假设A的结束位置为 aEnd,则aEnd+1~9就是B和C的位置,在这个位置范围内,B最少占据了一半的数字,否则B/C就不能整除了,所以可以从aEnd+1~9的中间位置开始

确定B的结束位置,这时候可以从中间位置开始向后确定B的结束位置,一直到8的位置,确定了B的结束位置,则A、B、C三个数字的具体值就都可以确定了,判断是否符

合等式N=A+B/C,符合则输出。

以上确定B的结束位置的方法其实不怎么好,因为还是会浪费一些时间(自己模拟下就知道了),不过已经可以在规定的时间内得出答案了。

这里再介绍一种确定B结束位置的方法,可以让性能再提高一些:

观察等式:N=A+B/C,可以转换成==》B=(N-A)*C,N和A确定了(先确定A的结束位置后,再来确定B的结束位置的),C的最后一个数字确定了(整个9位数最后一位),即可以确定B最后一个数字了(这里将其定义为BL),这样可以从以上的aEnd+1~9的中间位置开始找,直到8,当数字为BL时,则判读是否符合等式:N=A+B/C,可以想想,其实这种等于BL的位置至多有一次(因为数字1到9不能重复出现),所以第一次找到和BL匹配的数字的时候就不用再往后找了。用这种方法,提高的性能还是非常可观的!

思想其实挺简单,想不到打成字这么麻烦,将就着看吧,以下为java版的代码:

[java] view plaincopyprint?

  1. import java.util.Scanner;
  2. public class ys_09 {
  3. public static void main(String[] args) {
  4. //将等式定义为:N=A+B/C
  5. Scanner scanner=new Scanner(System.in);
  6. N=scanner.nextInt();
  7. long start=System.currentTimeMillis();
  8. int[] s=new int[]{1,2,3,4,5,6,7,8,9};
  9. NLength=(N+"").length();
  10. allRange(s, 0, s.length-1);
  11. long end=System.currentTimeMillis();
  12. System.out.println("耗时:"+(end-start)+" ms");
  13. System.out.println("总数为:"+kinds+" 种");
  14. }
  15. public static int N;
  16. public static int NLength;//N数字的长度
  17. public static int kinds;
  18. public static void process(int[] s){
  19. String str="";
  20. for(int i=0;i<9;i++) str+=s[i];
  21. int A,B,C,NMA,BC,BMCL,BLastNumber;
  22. //A的位数
  23. for(int i=1;i<=NLength;i++){
  24. /*
  25. //方法1
  26. A=Integer.valueOf(str.substring(0, i));
  27. NMA=N-A;//N减去A的值
  28. if(NMA<=0)return;
  29. BC=9-i;//B和C还有多少为可用
  30. BMCL=(NMA+"").length();//B/C的长度
  31. //确定的B的结束为止
  32. for(int j=i+BC/2;j<=8;j++){//可以优化这里
  33. B=Integer.valueOf(str.substring(i,j));
  34. C=Integer.valueOf(str.substring(j,9));
  35. if(B%C==0&&B/C==NMA){
  36. kinds++;
  37. System.out.println(N+"="+A+"+"+B+"/"+C);
  38. }
  39. }
  40. */
  41. //方法2
  42. A=Integer.valueOf(str.substring(0, i));
  43. NMA=N-A;
  44. if(NMA<=0)return;
  45. BC=9-i;//B和C总共多少位
  46. BLastNumber=(NMA*s[8])%10;//B最后的数字
  47. //j为B最后一个数字的位置
  48. //B最少占有B和C全部数字的一半,否则B/C不可能为整数
  49. for(int j=i+BC/2-1;j<=7;j++){
  50. //找到符合的位置
  51. if(s[j]==BLastNumber){
  52. B=Integer.valueOf(str.substring(i,j+1));
  53. C=Integer.valueOf(str.substring(j+1,9));
  54. if(B%C==0&&B/C==NMA){
  55. kinds++;
  56. System.out.println(N+"="+A+"+"+B+"/"+C);
  57. }
  58. //符合要求的位置只可能出现一次
  59. break;
  60. }
  61. }
  62. }
  63. }
  64. public static void swap(int[] s,int a,int b){
  65. if(a==b)return;
  66. int tmp=s[a];
  67. s[a]=s[b];
  68. s[b]=tmp;
  69. }
  70. //全排列
  71. public static void allRange(int[] s,int k,int m){
  72. if(k==m){
  73. process(s);
  74. return;
  75. }
  76. else{
  77. for(int i=k;i<=m;i++){
  78. swap(s,k,i);
  79. allRange(s, k+1, m);
  80. swap(s,k,i);
  81. }
  82. }
  83. }
  84. }
import java.util.Scanner;

public class ys_09 {

	public static void main(String[] args) {
		//将等式定义为:N=A+B/C
		Scanner scanner=new Scanner(System.in);
		N=scanner.nextInt();
		long start=System.currentTimeMillis();
		int[] s=new int[]{1,2,3,4,5,6,7,8,9};
		NLength=(N+"").length();
		allRange(s, 0, s.length-1);
		long end=System.currentTimeMillis();
		System.out.println("耗时:"+(end-start)+" ms");
		System.out.println("总数为:"+kinds+" 种");
	}
	public static int N;
	public static int NLength;//N数字的长度
	public static int kinds;
	public static void process(int[] s){
		String str="";
        for(int i=0;i<9;i++) str+=s[i];
		int A,B,C,NMA,BC,BMCL,BLastNumber;
		//A的位数
		for(int i=1;i<=NLength;i++){

			/*
			//方法1
			A=Integer.valueOf(str.substring(0, i));
			NMA=N-A;//N减去A的值
			if(NMA<=0)return;
			BC=9-i;//B和C还有多少为可用
			BMCL=(NMA+"").length();//B/C的长度
			//确定的B的结束为止
			for(int j=i+BC/2;j<=8;j++){//可以优化这里
				B=Integer.valueOf(str.substring(i,j));
				C=Integer.valueOf(str.substring(j,9));
				if(B%C==0&&B/C==NMA){
					kinds++;
					System.out.println(N+"="+A+"+"+B+"/"+C);
				}
			}
			*/

			//方法2
			A=Integer.valueOf(str.substring(0, i));
			NMA=N-A;
			if(NMA<=0)return;
			BC=9-i;//B和C总共多少位

			BLastNumber=(NMA*s[8])%10;//B最后的数字
			//j为B最后一个数字的位置
			//B最少占有B和C全部数字的一半,否则B/C不可能为整数
			for(int j=i+BC/2-1;j<=7;j++){
				//找到符合的位置
				if(s[j]==BLastNumber){
					B=Integer.valueOf(str.substring(i,j+1));
					C=Integer.valueOf(str.substring(j+1,9));
					if(B%C==0&&B/C==NMA){
						kinds++;
						System.out.println(N+"="+A+"+"+B+"/"+C);
					}
					//符合要求的位置只可能出现一次
					break;
				}
			}
		}
	}
	public static void swap(int[] s,int a,int b){
		if(a==b)return;
		int tmp=s[a];
		s[a]=s[b];
		s[b]=tmp;
	}
	//全排列
	public static void allRange(int[] s,int k,int m){
		if(k==m){
			process(s);
			return;
		}
		else{
			for(int i=k;i<=m;i++){
				swap(s,k,i);
				allRange(s, k+1, m);
				swap(s,k,i);
			}
		}
	}
}

输入:100

输出:

[java] view plaincopyprint?

    1. 100=3+69258/714
    2. 100=82+3546/197
    3. 100=81+5643/297
    4. 100=81+7524/396
    5. 100=94+1578/263
    6. 100=96+2148/537
    7. 100=96+1428/357
    8. 100=96+1752/438
    9. 100=91+5742/638
    10. 100=91+5823/647
    11. 100=91+7524/836
    12. 耗时:554 ms
    13. 总数为:11 种
时间: 2024-10-15 20:33:15

第四届蓝桥杯编程题:带分数 (转)的相关文章

第四届蓝桥杯真题 连号区间

本来想练习并查集,然后在看官网提示这是并查集类型题目,上来先默写了一下并查集,想了半天并查集怎么写..我呸,并查集..这里找规律..区间的[最大值-最小值]=[区间长度],直接枚举...讨厌这种找规律的题... #include <iostream> #include <algorithm> #include <cstdlib> #include <cstdio> using namespace std; const int maxn = 50000 + 1

第四届蓝桥杯 c/c++真题

第四届蓝桥杯 c/c++真题 <1>高斯日记 问题 大数学家高斯有个好习惯:无论如何都要记日记. 他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210 后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天.这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢? 高斯出生于:1777年4月30日. 在高斯发现的一个重要定理的日记上标注着:5343,因此可算出那天是:1791年12月15日. 高斯获得博士学位的那天日记上标着:

蓝桥杯——真题训练之蚂蚁感冒

标题:蚂蚁感冒 长100厘米的细长直杆子上有n只蚂蚁.它们的头有的朝左,有的朝右. 每只蚂蚁都只能沿着杆子向前爬,速度是1厘米/秒. 当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行. 这些蚂蚁中,有1只蚂蚁感冒了.并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁. 请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒. [数据格式] 第一行输入一个整数n (1 < n < 50), 表示蚂蚁的总数. 接着的一行是n个用空格分开的整数 Xi (-100 < Xi < 100),

蓝桥杯——真题训练之李白打酒

标题:李白打酒 话说大诗人李白,一生好饮.幸好他从不开车. 一天,他提着酒壶,从家里出来,酒壶中有酒2斗.他边走边唱: 无事街上走,提壶去打酒. 逢店加一倍,遇花喝一斗. 这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了. 请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b.则:babaabbabbabbbb 就是合理的次序.像这样的答案一共有多少呢?请你计算出所有可能方案? public class Main{ public static void m

算法笔记_108:第四届蓝桥杯软件类省赛真题(JAVA软件开发本科A组)试题解答

 目录 1 世纪末的星期 2 振兴中华 3 梅森素数 4 颠倒的价牌 5 三部排序 6 逆波兰表达式 7 错误票据 8 带分数 9 剪格子 10 大臣的旅费 前言:以下试题解答代码部分仅供参考,若有不当之处,还请路过的同学提醒一下~ 1 世纪末的星期 标题: 世纪末的星期 曾有邪教称1999年12月31日是世界末日.当然该谣言已经不攻自破. 还有人称今后的某个世纪末的12月31日,如果是星期一则会.... 有趣的是,任何一个世纪末的年份的12月31日都不可能是星期一!! 于是,"谣言制造商&qu

算法笔记_204:第四届蓝桥杯软件类决赛真题(Java语言C组)

目录 1 好好学习 2 埃及分数 3 金蝉素数 4 横向打印二叉树 5 危险系数 6 公式求值   1 好好学习 汤姆跟爷爷来中国旅游.一天,他帮助中国的小朋友贴标语.他负责贴的标语是分别写在四块红纸上的四个大字:"好.好.学.习".但是汤姆不认识汉字,他就想胡乱地贴成一行. 请你替小汤姆算一下,他这样乱贴,恰好贴对的概率是多少? 答案是一个分数,请表示为两个整数比值的形式.例如:1/3 或 2/15 等. 如果能够约分,请输出约分后的结果. 注意:不要书写多余的空格. 请严格按照格式

算法笔记_203:第四届蓝桥杯软件类决赛真题(C语言B组)

目录 1 猜灯谜 2 连续奇数和 3 空白格式化 4 高僧斗法 5 格子刷油漆 6 农场阳光   前言:以下代码仅供参考,若有错误欢迎指正哦~ 1 猜灯谜 标题:猜灯谜 A 村的元宵节灯会上有一迷题: 请猜谜 * 请猜谜 = 请边赏灯边猜 小明想,一定是每个汉字代表一个数字,不同的汉字代表不同的数字. 请你用计算机按小明的思路算一下,然后提交"请猜谜"三个字所代表的整数即可. 请严格按照格式,通过浏览器提交答案. 注意:只提交一个3位的整数,不要写其它附加内容,比如:说明性的文字. 9

第四届蓝桥杯第七题 错误票据

题解:写了简单题,好开心,我果然不适合学算法.......写写简单题,当个咸鱼就好了嘛!! #include <iostream> #include <algorithm> #include <cstdlib> #include <cstdio> #include <cstring> #include <sstream> using namespace std; const int maxn = 100000 + 50; int N;

第四届蓝桥杯第八题 翻硬币

题解:简单贪心, 比赛之前写写水题 #include <iostream> #include <cstring> #include <string> using namespace std; const int maxn = 1000 + 20; void solve() { int init[maxn], over[maxn]; string in, ov; int ans = 0; cin >> in; cin >> ov; for (uns