暴力求解系列之简单枚举
题目来自刘汝佳的《算法竞赛入门经典(第二版)》,下面实现代码均为Java
简单枚举
问题1:
输入正整数 n, 按从小到大的顺序输出所有形如 abcde/fghij=n的表达式, 其中 a~j恰好为数字 0~9的一个排列(可以有前导0),其中 2≤n≤79
解题思路:直接枚举所有 0~9的所有排列,枚举量为 10!=3628800,可以只枚举 fghij,枚举量下降至不到10万
下面是枚举 fghij的两种解法:
解法1是直接枚举 1000至 49999的所有数,枚举会有重复元素,枚举量为4万+;
解法2是递归枚举(解答树),没有枚举重复元素,枚举量为 10∗9∗8∗7∗6=30240。
public class Division {
/**
* 算法1实现
* @param n 正整数
* @return 所有满足abcde/fghij = n的abcdefghij列表
*/
private List<String> solver1(int n){
List<String> results = new ArrayList<>();
for(Integer i = 1000;i<=49999;i++){
String divisor = new String(i.toString());
if(divisor.length() < 5){
divisor = "0" + divisor;
}
String dividend = new String(new Integer(i*n).toString());
/* 如果位数共大于10 */
if(dividend.length() + divisor.length() > 10){
continue;
}
Set<Character> countSet = new HashSet<>();
for(int j = 0;j<dividend.length();j++){
countSet.add(dividend.charAt(j));
}
if(countSet.size() < 5){
continue;
}
for(int j = 0;j<divisor.length();j++){
countSet.add(divisor.charAt(j));
}
if(countSet.size() == 10){
results.add(dividend + "/" + divisor + "=" + n);
}
}
return results;
}
/**
* 算法2实现:递归枚举
* @param n 输入
* @param results 保存所有结果
* @param temp 辅助数组,记录当前已经用过的元素
* @param cur 记录当前已经用过的元素个数
*/
private void solver2(int n, List<String> results, Integer[] temp, int cur){
if(cur == 5){
String s = "";
Set<Integer> countSet = new HashSet<>(Arrays.asList(temp));
for(int i = 0;i<5;i++){
s += temp[i];
}
String dividend = new Integer(Integer.parseInt(s) * n).toString();
if(dividend.length() == 5){
for(int j = 0;j<dividend.length();j++){
countSet.add(Integer.parseInt(dividend.substring(j,j+1)));
}
if(countSet.size() == 10){
results.add(dividend + "/" + s + "=" + n);
}
}
}else{
for(int i = 0;i<=9;i++){
boolean flag = true;
for(int j = 0;j<cur;j++){
if(temp[j] == i){
flag = false;
break;
}
}
if(flag){
temp[cur] = i;
solver2(n, results, temp, cur+1);
}
}
}
}
/**
* 输出结果
*/
public void solution(){
int n = 3;
/* 解法1 */
for(String s:solver1(n)){
System.out.println(s);
}
System.out.println();
/* 解法2 */
List<String> results = new ArrayList<>();
Integer[] temp = new Integer[5];
Arrays.fill(temp, -1);
solver2(n, results, temp, 0);
for(String s:results){
System.out.println(s);
}
}
public static void main(String[] args){
Division division = new Division();
division.solution();
}
}
问题2:
输入正整数 k, 找到所有的正整数 x≥y,使得 k1=x1+y1
解题思路:首先发现 x=y=2k时等式成立,因此只要枚举 k<y≤2k即可,根据 k和 y判断求出的 x=y−kk∗y是否为整数
/**
* 算法实现
* @param k 正整数k
* @return 所有满足1/k = 1/x + 1/y的x和y的列表
*/
private List<int[]> solver1(int k){
List<int[]> results = new ArrayList<>();
/* 枚举y */
for(int y = k+1;y<=2*k;y++){
double temp = (double) k*y/(y-k);
if(temp == (int) temp){
results.add(new int[]{(int) temp, y});
}
}
return results;
}
/**
* 输出结果
*/
public void solution(){
/* 输入 */
int k = 12;
for(int[] result:solver1(k)){
System.out.println("1/" + result[0] + " + " + "1/" + result[1] + " = " + "1/" + k);
}
}
public static void main(String[] args){
Fractions fractions = new Fractions();
fractions.solution();
}
问题3:
输入 n个元素组成的序列 S, 你需要找出一个乘积最大的连续子序列。 如果这个最大的乘积不是正数, 应输出 0。 其中 1≤n≤18, −10≤Si≤10。
解题思路:直接枚举起点 i到终点 j的乘积,找出最大值即可(虽然所有元素的绝对值都小于等于10,其最大乘积可能会溢出,这里我没管了= =)
public class MaxmiumProduct {
/**
* 算法实现
* @param a 包含所有元素
* @return 连续子序列最大乘积
*/
private long solver1(int[] a){
int n = a.length;
long[][] product = new long[n][n];
long maxProduct = 0;
for(int i = 0;i<n;i++){
long tempProduct = 1L;
for(int j = i;j<n;j++){
product[i][j] = tempProduct *= a[j];
if(maxProduct < product[i][j]){
maxProduct = product[i][j];
}
}
}
return maxProduct;
}
/**
* 输出结果
*/
public void solution(){
/* 输入 */
int a[] = new int[]{2, 4, -3, 2, 0, -2, -4, -8, 0, 4, 9, -5, -10} ;
System.out.println(solver1(a));
}
public static void main(String[] args){
MaxmiumProduct maxmiumProduct = new MaxmiumProduct();
maxmiumProduct.solution();
}
}
有任何问题,欢迎指出 :)