暴力破解的缺点:
- 循环次数过多
- 多层循环容易造成分支数爆炸
- 递归也会引发指数增长
剪枝:
- 尽早结束无意义的运算分支
- 尽量选择分支少的为循环条件
- 优点: 提高计算速度和效率
实例分析:
1.已知要找的总额,和提供的各种面值
例如:要找8元零钱,可以使用5元,2元,1元和5角组合。
代码:
public static void main(String[] args) { int sum = 80; for (int a = 0; a <= sum / 50; ++a) { for (int b = 0; b <= sum / 20; ++b) { for (int c = 0; c <= sum / 10; ++c) { for (int d = 0; d <= sum / 5; ++d) { if (a * 50 + b * 20 + c * 10 + d * 5 == sum) { System.out.println(a + "," + b + "," + c + "," + d); } } } } } }
这个问题本身不复杂,但是如果将问题复杂化,例如:要找800元零钱,有几十种零钞。循环嵌套的层数就会很深,计算的时间就会非常久。
剪枝版:
public static void main(String[] args) { int sum = 80; for (int a = 0; a <= sum / 50; ++a) { for (int b = 0; b <= sum / 20; ++b) { if (80 - 50 * a - 20 * b < 0) //剪枝 break; for (int c = 0; c <= sum / 10; ++c) { int d = (sum - a * 50 - b * 20 - c * 10); if (d < 0) //剪枝 break; d = d / 5; // d直接根据a,b,c的结果计算 if (a * 50 + b * 20 + c * 10 + d * 5 == sum) { System.out.println(a + "," + b + "," + c + "," + d); } } } } }
2. n位数的平方的尾数是n位数本身
代码:
public static void main(String[] args) { for (int i = 100; i < 1000; ++i) { int n = i * i; // 3位数的平方 int m = n % 1000; // 3位数平方的尾数 if (m == i) System.out.println(i+"," + n); } }
剪枝版:
public static void main(String[] args) { for (int i = 100; i < 1000; ++i) { int n = i * i; // 三位数的平方 int m = n % 1000; // 3位数平方的尾数 int geWei = n % 10; // 三位数平方后的个位 // 剪枝 过滤 if (geWei != 0 && geWei != 1 && geWei != 5 && geWei != 6) { continue; } if (m == i) System.out.println(i+"," + n); } }
3.观察下面的算式:
△△△ * △△ = △△△△
某3位数乘以2位数,结果为4位数
要求:在9个△所代表的数字中,1~9的数字恰好每个出现1次
代码:
public static void main(String[] args) { { long beginTime = System.currentTimeMillis(); int n1, n2, n3, n4, n5, n6, n7, n8, n9; for (int i = 12; i <= 98; ++i) { n1 = i / 10; n2 = i % 10; if (n2 == 0 || n1 == n2) continue; for (int j = 123; j <= 987; ++j) { n3 = j / 100; n4 = j / 10 % 10; n5 = j % 10; if (n4 == 0 || n5 == 0) continue; if (n3 == n4 || n3 == n5 || n4 == n5) continue; if (n3 == n1 || n3 == n2 || n4 == n1 || n4 == n2 || n5 == n1 || n5 == n2) continue; int k = i * j; if (k < 1234 || k > 9876) continue; n6 = k / 1000; n7 = k / 100 % 10; n8 = k / 10 % 10; n9 = k % 10; if (n7 == 0 || n8 == 0 || n9 == 0) continue; if (n6 == n7 || n6 == n8 || n6 == n9 || n7 == n8 || n7 == n9 || n8 == n9) continue; if (n6 == n1 || n6 == n2 || n6 == n3 || n6 == n4 || n6 == n5) continue; if (n7 == n1 || n7 == n2 || n7 == n3 || n7 == n4 || n7 == n5) continue; if (n8 == n1 || n8 == n2 || n8 == n3 || n8 == n4 || n8 == n5) continue; if (n9 == n1 || n9 == n2 || n9 == n3 || n9 == n4 || n9 == n5) continue; System.out.println(j + "," + i + "," + k); } } long endTime = System.currentTimeMillis(); System.out.println("用时: " + (endTime - beginTime) + " ms"); }
效果:
使用HashSet
public static void main(String[] args) { Set<Integer> set = new HashSet<Integer>(); int n1, n2, n3, n4, n5, n6, n7, n8, n9; long beginTime = System.currentTimeMillis(); for (int i = 123; i <= 987; ++i) { set.clear(); n1 = i / 100; n2 = i / 10 % 10; n3 = i % 10; if (n2 == 0 || n3 == 0) // 不能为0 continue; if (n1 == n2 || n1 == n3 || n2 == n3) // 不能重复 continue; set.add(n1); set.add(n2); set.add(n3); for (int j = 12; j <= 98; ++j) { n4 = j / 10; n5 = j % 10; if (n5 == 0) continue; if (n4 == n5) continue; if (set.contains(n4) || set.contains(n5)) continue; int k = i * j; if (k < 1234 || k > 9876) continue; n6 = k / 1000; n7 = k / 100 % 10; n8 = k / 10 % 10; n9 = k % 10; if (n6 == 0 || n7 == 0 || n8 == 0 || n9 == 0) { continue; } if (n6 == n7 || n6 == n8 || n6 == n9 || n7 == n8 || n7 == n9 || n8 == n9) { continue; } set.add(n4); set.add(n5); if (set.contains(n6) || set.contains(n7) || set.contains(n8) || set.contains(n9)) { set.remove(n4); set.remove(n5); continue; } System.out.println(i + "," + j + "," + k); set.remove(n4); set.remove(n5); } } long endTime = System.currentTimeMillis(); System.out.println("总计用时:" + (endTime - beginTime) + "ms"); }
效果: