1 问题描述

在8枚外观相同的硬币中,有一枚是假币,并且已知假币与真币的重量不同,但不知道假币与真币相比较轻还是较重。可以通过一架天平来任意比较两组硬币,设计一个高效的算法来检测这枚假币。

2.1 减治法原理叙述

在说减法法原理之前,我们先来简单看看分治法原理:分治法是把一个大问题划分为若干子问题,分别求解子问题,然后再把子问题的解进行合并得到原问题的解。

而减治法同样是把大问题分解成为若干个子问题,但是这些子问题不需要分别求解,只需求解其中的一个子问题,也无需对子问题进行合并。换种说法,可以说减治法是退化的分治法。

减治法原理正式描述:减治法(reduce and conquer method)将原问题的解分解为若干个子问题,并且原问题的解与子问题的解之间存在某种确定关系,如果原问题的规模为n,则子问题的规模通常是n/2 或n-1。

2.2 8枚硬币规模解法

求解思路:

(1)首先输入8枚硬币重量,存放在一个长度为8的一维数组中。

(2)定义a,b,c,d,e,f,g,h八个变量,分别对应一枚硬币的重量。然后把这8枚硬币分成三组,分别为abc(abc = a+b+c)、def(def = d+e+f)、gh。

(3)比较adc和def的大小。如果abc = def,则假币必定是g或者h,然后把g和h分别与真币a进行比较大小,从而得到假币。如果abc > def,则g和h必定为真币,然后比较ae(ae = a+e)和bd(bd = b+d)大小(PS:此处意思为ae = abc - c并把b和e交换位置,bd = def - f并把e和b交换位置),如果ae = bd,则假币必定是c或者f,然后依次与g比较,从而得到假币;如果ae > bd,则假币必定是a或者d,然后依次与g比较,从而得到假币;如果ae < bd,则假币必定是e或者b,然后依次与g比较,从而得到假币。

(4)abc < def情况参照(3)中思想求解,最终得到假币。

具体程序流程图如图1所示:

  1. package com.liuzhen.coin;
  2. import java.util.*;
  3. public class EightCoins {
  4. public static void printFakeCoin(int [] A){
  5. int a,b,c,d,e,f,g,h; //八枚硬币重量
  6. a = A[0];
  7. b = A[1];
  8. c = A[2];
  9. d = A[3];
  10. e = A[4];
  11. f = A[5];
  12. g = A[6];
  13. h = A[7];
  14. int abc = a+b+c;
  15. int def = d+e+f;
  16. //当abc重量大于def重量时,找出其中假币,并打印输出
  17. if(abc > def){
  18. if(a+e > b+d){ //此时,假币必定为a或者d
  19. if(a > g)
  20. System.out.println("假币为第1枚硬币,较重,重量为:"+a);
  21. else{
  22. if(a < g)
  23. System.out.println("假币为第1枚硬币,较轻,重量为:"+a);
  24. else{
  25. int test = d-g;
  26. if(test > 0)
  27. System.out.println("假币为第4枚硬币,较重,重量为:"+d);
  28. else
  29. System.out.println("假币为第4枚硬币,较轻,重量为:"+d);
  30. }
  31. }
  32. }
  33. if(a+e == b+d){ //此时,假币必定为c或者f
  34. if(c > g)
  35. System.out.println("假币为第3枚硬币,较重,重量为:"+c);
  36. else{
  37. if(c < g)
  38. System.out.println("假币为第3枚硬币,较轻,重量为:"+c);
  39. else{
  40. int test = f-g;
  41. if(test > 0)
  42. System.out.println("假币为第6枚硬币,较重,重量为:"+f);
  43. else
  44. System.out.println("假币为第6枚硬币,较轻,重量为:"+f);
  45. }
  46. }
  47. }
  48. if(a+e < b+d){ //此时,假币必定为b或者e
  49. if(b > g)
  50. System.out.println("假币为第2枚硬币,较重,重量为:"+b);
  51. else{
  52. if(c < g)
  53. System.out.println("假币为第2枚硬币,较轻,重量为:"+b);
  54. else{
  55. int test = e-g;
  56. if(test > 0)
  57. System.out.println("假币为第5枚硬币,较重,重量为:"+e);
  58. else
  59. System.out.println("假币为第5枚硬币,较轻,重量为:"+e);
  60. }
  61. }
  62. }
  63. }
  64. //当abc重量等于def重量时,则假币必定为g或者h
  65. if(abc == def){
  66. if(g > a)
  67. System.out.println("假币为第7枚硬币,较重,重量为:"+g);
  68. else{
  69. if(g < a)
  70. System.out.println("假币为第7枚硬币,较轻,重量为:"+g);
  71. else{
  72. int test = h-a;
  73. if(test > 0)
  74. System.out.println("假币为第8枚硬币,较重,重量为:"+h);
  75. else
  76. System.out.println("假币为第8枚硬币,较轻,重量为:"+h);
  77. }
  78. }
  79. }
  80. //当abc重量小于def重量时,找出其中假币,并打印输出
  81. if(abc < def){
  82. if(a+e > b+d){ //此时,假币必定为b或者e
  83. if(b > g)
  84. System.out.println("假币为第2枚硬币,较重,重量为:"+b);
  85. else{
  86. if(b < g)
  87. System.out.println("假币为第2枚硬币,较轻,重量为:"+b);
  88. else{
  89. int test = e-g;
  90. if(test > 0)
  91. System.out.println("假币为第5枚硬币,较重,重量为:"+e);
  92. else
  93. System.out.println("假币为第5枚硬币,较轻,重量为:"+e);
  94. }
  95. }
  96. }
  97. if(a+e == b+d){ //此时,假币必定为c或者f
  98. if(c > g)
  99. System.out.println("假币为第3枚硬币,较重,重量为:"+c);
  100. else{
  101. if(c < g)
  102. System.out.println("假币为第3枚硬币,较轻,重量为:"+c);
  103. else{
  104. int test = f-g;
  105. if(test > 0)
  106. System.out.println("假币为第6枚硬币,较重,重量为:"+f);
  107. else
  108. System.out.println("假币为第6枚硬币,较轻,重量为:"+f);
  109. }
  110. }
  111. }
  112. if(a+e < b+d){ //此时,假币必定为a或者d
  113. if(a > g)
  114. System.out.println("假币为第1枚硬币,较重,重量为:"+a);
  115. else{
  116. if(a < g)
  117. System.out.println("假币为第1枚硬币,较轻,重量为:"+a);
  118. else{
  119. int test = d-g;
  120. if(test > 0)
  121. System.out.println("假币为第4枚硬币,较重,重量为:"+d);
  122. else
  123. System.out.println("假币为第4枚硬币,较轻,重量为:"+d);
  124. }
  125. }
  126. }
  127. }
  128. }
  129. public static void main(String args[]){
  130. Scanner scan = new Scanner(System.in);
  131. int[] weightCoin = new int[8];
  132. System.out.println("请您输入8枚硬币的重量(其中有一枚假币,其它硬币重量均相同):");
  133. for(int i = 0; i < 8; i++)
  134. weightCoin[i] = scan.nextInt();
  135. printFakeCoin(weightCoin);
  136. }
  137. }



2.3 n枚硬币规模解法

求解思路:

此处我写了两个方法:

方法1:

  1. /*返回一个长度为3的一维数组,result[0]表示假币轻重,值为0表示偏轻,值为1表示偏重;result[1]表示一枚真币的重量;
  2. result[2]表示,当硬币个数为奇数且最后一枚为假币时,把这枚假币重量赋值给result[2],否则result[2]值为0*/
  3. public static int[] getJudgeCoinArray(int[] A);

方法2:

  1. /*返回一个长度为3的一维数组,result[0]表示假币轻重,值为0表示偏轻,值为1表示偏重;result[1]表示假币的重量;
  2. result[2]表示假币在硬币数组中的具体位置*/
  3. public static int[] getFakeCoin(int[] A,int min,int max,int judge,int real);

具体程序流程图如图3所示(PS:此处图画的不完整,主要是表达程序的思想,不要纠结哟):

  1. package com.liuzhen.coin;
  2. import java.util.Scanner;
  3. public class NCoins {
  4. /*返回一个长度为3的一维数组,result[0]表示假币轻重,值为0表示偏轻,值为1表示偏重;result[1]表示假币的重量;
  5. result[2]表示假币在硬币数组中的具体位置*/
  6. public static int[] getFakeCoin(int[] A,int min,int max,int judge,int real){
  7. int[] result = new int[3]; //定义一个长度为3的一维数组,初始化所有值为0
  8. if((max-min)%2 == 1){
  9. //当max-min为奇数时,不能完成二分,判断A[max-1]是否为假币,若是,则直接返回结果,否则执行max= max-1
  10. if(A[max-1] != real){
  11. result[0] = judge;
  12. result[1] = A[max-1];
  13. result[2] = max;
  14. return result;
  15. }
  16. max = max-1;
  17. }
  18. if(max-min == 2){
  19. //当max-min为2时,此时只剩下两枚硬币,可以直接比较,找出假币
  20. int a = A[min]-real;
  21. int b = A[max-1]-real;
  22. if(a != 0){
  23. result[0] = judge;
  24. result[1] = A[min];
  25. result[2] = min+1;
  26. }
  27. if(b != 0){
  28. result[0] = judge;
  29. result[1] = A[max-1];
  30. result[2] = max;
  31. }
  32. return result;
  33. }
  34. int sum1 = 0,sum2 = 0;
  35. // System.out.println("max-min值为:"+(max-min));
  36. // System.out.println("judge值为:"+judge);
  37. for(int i = 0;i<(max-min)/2;i++){
  38. sum1 += A[min+i]; //二分后的左半部分硬币总重量
  39. sum2 += A[(max+min)/2+i]; //二分后的右半部分硬币总重量
  40. }
  41. // System.out.println("sum1值为:"+sum1);
  42. // System.out.println("sum2值为:"+sum2);
  43. //假币较重
  44. if(judge == 1){
  45. if(sum1 > sum2)
  46. max = (max+min)/2; //此时假币在左半部分
  47. else
  48. min = (max+min)/2; //此时假币在右半部分
  49. }
  50. //假币较轻
  51. if(judge == 0){
  52. if(sum1 > sum2)
  53. min = (max+min)/2; //此时假币在右半部分
  54. else
  55. max = (max+min)/2; //此时假币在左半部分
  56. }
  57. // System.out.println("min值为:"+min);
  58. // System.out.println("max值为:"+max);
  59. result = getFakeCoin(A,min,max,judge,real); //递归求解最终假币结果
  60. return result;
  61. }
  62. /*返回一个长度为3的一维数组,result[0]表示假币轻重,值为0表示偏轻,值为1表示偏重;result[1]表示一枚真币的重量;
  63. result[2]表示,当硬币个数为奇数且最后一枚为假币时,把这枚假币重量赋值给result[2],否则result[2]值为0*/
  64. public static int[] getJudgeCoinArray(int[] A){
  65. int[] result = new int[3]; //定义一个长度为3的一维数组,初始化所有值为0
  66. int len = A.length; //获取数组A的长度,即硬币的总个数
  67. int a = A[len-1]; //最后一枚硬币重量,用于判断当硬币个数为奇数时,最后一枚硬币时假币的情况
  68. if(len%2 == 1){ //当硬币总个数为奇数时,将硬币总个数减1,变成偶数
  69. len = len-1;
  70. }
  71. int[] Left1 = new int[len/2]; //二分左半部分
  72. int[] Right1 = new int[len/2]; //二分右半部分
  73. int sum1 = 0,sum2 = 0;
  74. for(int i = 0;i<len/2;i++){
  75. Left1[i] = A[i];
  76. Right1[i] = A[len/2+i];
  77. sum1 += A[i]; //左半部分硬币总重量
  78. sum2 += A[len/2+i]; //右半部分硬币总重量
  79. }
  80. // System.out.println("sum1值为:"+sum1);
  81. // System.out.println("sum2值为:"+sum2);
  82. int sum3=0,sum4=0;
  83. int len1 = Left1.length; //获取二分后左边数组长度
  84. int b = Left1[len1-1]; //当Left1数组长度为奇数时,用于判断最后一枚硬币是否为假币
  85. if(len1%2 == 1) //当len1为奇数时,将len1减1,变成偶数
  86. len1 = len1-1;
  87. for(int j = 0;j<len1/2;j++){
  88. sum3 += Left1[j]; //左半部分的左半部分总重量
  89. sum4 += Left1[len1/2+j]; //左半部分的右半部分总重量
  90. }
  91. //当左半部分硬币重量大于右半部分重量时
  92. if(sum1 > sum2){
  93. if(sum3 == sum4){
  94. if(b > Left1[0]){ //此时可判断b为假币,且较重
  95. result[0] = 1;
  96. result[1] = Left1[0];
  97. result[2] = b;
  98. return result;
  99. }
  100. if(b < Left1[0]){ //此时可判断b为假币,且较轻
  101. result[0] = 0;
  102. result[1] = Left1[0];
  103. result[2] = b;
  104. return result;
  105. }
  106. //否则,假币在Right1中,且较轻
  107. result[0] = 0;
  108. result[1] = Left1[0];
  109. }
  110. if(sum3 != sum4){
  111. //假币在Left1中,且较重
  112. result[0] = 1;
  113. result[1] = Right1[0];
  114. }
  115. }
  116. //当左半部分硬币重量小于右半部分重量时
  117. if(sum1 < sum2){
  118. if(sum3 == sum4){
  119. if(b > Left1[0]){ //此时可判断b为假币,且较重
  120. result[0] = 1;
  121. result[1] = Left1[0];
  122. result[2] = b;
  123. return result;
  124. }
  125. if(b < Left1[0]){ //此时可判断b为假币,且较轻
  126. result[0] = 0;
  127. result[1] = Left1[0];
  128. result[2] = b;
  129. return result;
  130. }
  131. //否则,假币在Right1中,且较重
  132. result[0] = 1;
  133. result[1] = Left1[0];
  134. }
  135. if(sum3 != sum4){
  136. //假币在Left1中,且较轻
  137. result[0] = 0;
  138. result[1] = Right1[0];
  139. }
  140. }
  141. //当左半部分硬币重量等于右半部分重量时
  142. if(sum1 == sum2){
  143. if(a > Left1[0]){ //此时可判断a为假币,且较重
  144. result[0] = 1;
  145. result[1] = Left1[0];
  146. result[2] = a;
  147. return result;
  148. }
  149. if(a < Left1[0]){ //此时可判断a为假币,且较轻
  150. result[0] = 0;
  151. result[1] = Left1[0];
  152. result[2] = a;
  153. return result;
  154. }
  155. }
  156. // System.out.println("sum3值为:"+sum3);
  157. // System.out.println("sum4值为:"+sum4);
  158. return result;
  159. }
  160. public static void main(String args[]){
  161. Scanner sc = new Scanner(System.in);
  162. System.out.println("请您输入n枚硬币的总个数:");
  163. int n = sc.nextInt();
  164. int[] A = new int[n];
  165. System.out.println("请您输入n枚硬币的重量(其中有一枚假币,其它硬币重量均相同):");
  166. for(int i = 0; i < n; i++)
  167. A[i] = sc.nextInt();
  168. int[] result1 = getJudgeCoinArray(A);
  169. int len = A.length; //数组A的长度
  170. int judge = result1[0]; //假币轻重判断
  171. int real = result1[1]; //真币重量
  172. int fakeCoin = result1[2]; //假币重量
  173. System.out.println("硬币总个数为:"+len);
  174. System.out.println("judge值为:"+judge+"(1表示假币较重,0表示假币较轻)");
  175. System.out.println("真币重量为:"+real);
  176. if(fakeCoin != 0)
  177. System.out.println("假币重量为:"+fakeCoin);
  178. else{
  179. int[] result = getFakeCoin(A,0,len,judge,real);
  180. if(result[0] == 1)
  181. System.out.print("假币较重,");
  182. else
  183. System.out.print("假币较轻,");
  184. System.out.print("且假币是第"+result[2]+"硬币,");
  185. System.out.println("假币重量为:"+result[1]);
  186. }
  187. }
  188. }

Java实现8枚硬币问题(减治法)的更多相关文章

  1. n枚硬币问题(找假币)

    问题描述: 在n枚外观相同的硬币中,有一枚是假币,并且已知假币与真币的重量不同,但不知道假币与真币相比较轻还是较重.可以通过一架天平来任意比较两组硬币,设计一个高效的算法来检测这枚假币. 解题思路: ...

  2. Ex 6_19 至多用k枚硬币兑换价格_第七次作业

    子问题定义: 定义一个二维数组b,其中b[i][j]表示用i个硬币是否能兑换价格j,表示第i个币种的面值, 递归关系: 初值设定: 求解顺序: 按下标从小到大依次求解数组b每一列的值,最后二维数组b的 ...

  3. 【Java】【4】关于Java中的自增自减

    摘要:理解j = j++与j = ++j的区别:正确用法:直接用j++,不要用前两种 正文: import java.util.*; public class Test{ public static ...

  4. xth的第 12 枚硬币(codevs 1366)

    题目描述 Description 传说 xth 曾经拥有11枚完全相同硬币(你懂得),不过今年呢,rabbit又送了他一 枚硬币.这枚硬币和其他硬币外观相同,只有重量不同,或轻或重.Xth 一不小心, ...

  5. 1366 xth 的第 12 枚硬币

    1366 xth 的第 12 枚硬币  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题解       题目描述 Description 传说 xth 曾 ...

  6. java基础(二) 自增自减与贪心规则

    引言   JDK中提供了自增运算符++,自减运算符--.这两个操作符各有两种使用方式:前缀式(++ a,--a),后缀式(a++,a--).可能说到这里,说不得有读者就会吐槽说,前后缀式都挺简单的,前 ...

  7. Java练习demo 20190402 优惠券扣减

    实体类: package org.jimmy.autosearch2019.pojo; import java.math.BigDecimal; public class TestEntity2019 ...

  8. 关于java中自增,自减,和拓展运算符的小讨论

    java中运算符很多,但是能深入讨论的不算太多.这里我仅仅以++,*=为例做讨论. 例:++ i=0; i=i++ + ++i;//i=1 i=++i+i++;//i=2 i=i++ -++i;//i ...

  9. java操作时间,将当前时间减一年,减一天,减一个月

    在Java中操作时间的时候,常常遇到求一段时间内的某些值,或者计算一段时间之间的天数 Date date = new Date();//获取当前时间 Calendar calendar = Calen ...

随机推荐

  1. 新鲜出炉高仿网易云音乐 APP

    我的引语 晚上好,我是吴小龙同学,我的公众号「一分钟GitHub」会推荐 GitHub 上好玩的项目,一分钟 get 一个优秀的开源项目,挖掘开源的价值,欢迎关注我. 项目中成长是最快的,如何成长,就 ...

  2. C# 获取从1月至12月的月初时间和月末时间

    public IActionResult GetMonthData() { var dataList = new List<object>(); var currentMonth = Da ...

  3. 一文带你学会国产加密算法SM4的java实现方案

    前言 今天给大家带来一个国产SM4加密解密算法的java后端解决方案,代码完整,可以直接使用,希望给大家带来帮助,尤其是做政府系统的开发人员,可以直接应用到项目中进行加密解密. 画重点!是SM4哦,不 ...

  4. utf8mb4复杂昵称问题

    wechat_ling wl_channel_consumer nickname wl_consumer nickname alter table wl_channel_consumer modify ...

  5. kudu_遇到的一些问题

                            最近在研究,自己搭建kudu遇到的一些问题,及解决方法,供大家参考. 1.java连接kudu,出现超时的问题,是因为kudu开启了认证模式: 通过查找 ...

  6. xampp apache 安全性问题

    要禁止 Apache 显示目录结构列表,只需将 Option 中的 Indexes 去掉即可.<Directory "D:/Apa/blabla"> Options I ...

  7. tableView的嵌套

    1,subTableView需要开启多手势识别,多层tableView都会响应滚动事件(如果底层是scroll 依然会响应,这样滚动tableview时,scroll也会滚动,导致滚动过于灵活)2,通 ...

  8. E. Physical Education Lessons 动态开辟线段树区间更新

    E. Physical Education Lessons time limit per test 1 second memory limit per test 256 megabytes input ...

  9. Closures Basic

    Closures Closures are one of the most powerful features of JavaScript. JavaScript allows for the nes ...

  10. Pyqt5_QfileDialog

    QfileDialog getOpenFileName getSaveFileName getExistingDirectory getOpenFileName: 就是调用窗口来读取用户选取的文件路径 ...