Java数据结构与算法——经典趣题

1、百钱买百鸡

百钱买百鸡是一个非常经典的不定方程问题,最早源于我国古代的《算经》,这是古代著名数学家张丘建首次提出的。百钱买百鸡问题的原文如下:
鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁、母、雏各几何?

这个问题的大致意思是公鸡5文钱1只,母鸡3文钱1只,小鸡3只1文钱,如果用100文钱买100只鸡,那么公鸡、母鸡和小鸡各应该买多少只呢?

/**
 * 百鸡百钱
 * @param m 钱数
 * @param n 鸡数
 */
public static void BQBJ(int m, int n) {
    int x, y, z;
    for (x = 0; x <= n; x++) {//公鸡数量
        for (y = 0; y <= n; y++) {//母鸡数量
            z = n - x - y;//小鸡数量
            if (z > 0 && z % 3 == 0 && x * 5 + y * 3 + z / 3 == m) {
                System.out.printf("公鸡:%d只,母鸡:%d只,小鸡:%d只\n", x, y, z);
            } else {
                //无法求解
            }
        }
    }
}

public static void main(String[] args) {
    BQBJ(100, 100);
}

公鸡:0只,母鸡:25只,小鸡:75只
公鸡:4只,母鸡:18只,小鸡:78只
公鸡:8只,母鸡:11只,小鸡:81只
公鸡:12只,母鸡:4只,小鸡:84

2、五家共井

五家共井记载于我国古代的数学专著《九章算术》。五家共井问题的原文如下:
今有五家共井,甲二绠不足如乙一绠,乙三绠不足如丙一绠,丙四绠不足如丁一绠,丁五绠不足如戊一绠,戊六绠不足如甲一绠。如各得所不足一绠,皆逮。问井深、绠长各几何?

这里,“绠”即汲水桶上的绳索,“逮”即到达井底水面。这个问题的大致意思是现在有五家共用一口井,甲、乙、丙、丁、戊五家各有一条绳子汲水:甲绳×2+乙绳=井深,乙绳×3+丙绳=井深,丙绳×4+丁绳=井深,丁绳×5+戊绳=井深,戊绳×6+甲绳=井深,求甲、乙、丙、丁、戊各家绳子的长度和井深。

首先分析一下问题,设甲、乙、丙、丁、戊各家绳子的长度分别为lenl、len2、len3、len4、len5,井深为len,则前述问题五家共井的条件可表示为如下方程:

  • len1 X2+len2=len
  • len2X3+len3=len
  • len3 X4+len4=len
  • len4×5+len5=lem
  • len5X6+len1=len

由于这里有6个未知数,但只有5个方程。因此,这是一个不定方程问题,可能存在多个求解结果。可以进一步限定绳长和井深都是整数,来求解一个最小的整数结果。对上述几个式子进行变形,得到如下结果:

  • lenl×2+len2=len2X3+len3=len3×4+len4=len4×5+len5=len5X6+lenl
  • len1=len2+len3/2
  • len2=len3+len4/3
  • len3=len4+len5/4
  • len4=len5+len1/5

由此可知:

  • len3能被2整除,len3必为2的倍数;
  • len4能被3整除,len4必为3的倍数;
  • len5能被4整除,len5必为4的倍数;
  • len1能被5整除,len1必为5的倍数;
/**
 * 线性方程组求解
 */
public static int[] solveLinearEquations(int maxH) {
    for (int H = 1; H <= maxH; H++) {
        for (int e = 1; e <= H; e++) {
            int a = H - 6 * e;
            if (a <= 0) continue;

            int b = 12 * e - H;
            if (b <= 0) continue;

            int c = 4 * H - 36 * e;
            if (c <= 0) continue;

            int d = 144 * e - 15 * H;
            if (d <= 0) continue;

            // 验证所有方程
            if (2*a + b == H &&
                    3*b + c == H &&
                    4*c + d == H &&
                    5*d + e == H &&
                    6*e + a == H) {
                return new int[]{H, a, b, c, d, e};
            }
        }
    }
    return null;  // 未找到解
}


public static void main(String[] args) {
    System.out.println("线性方程组求解 (H ≤ 5000):");
    int[] linearSolution = solveLinearEquations(5000);
    if (linearSolution != null) {
        System.out.printf("找到解: H=%d, a=%d, b=%d, c=%d, d=%d, e=%d%n",
                linearSolution[0], linearSolution[1], linearSolution[2],
                linearSolution[3], linearSolution[4], linearSolution[5]);
    } else {
        System.out.println("在指定范围内未找到解");
    }
}

线性方程组求解 (H5000):
找到解: H=721, a=265, b=191, c=148, d=129, e=76

3、鸡兔同笼

鸡兔同笼问题最早记载于1500年前的《孙子算经》,这是我国古代一个非常著名的问题。

鸡兔同笼的原文如下:
今有鸡兔同笼,上有三十五头,下有九十四足,问鸡兔各几何?

这个问题的大致意思是在一个笼子里关着若干只鸡和若干只兔,从上面数共有35个头:从下面数共有94只脚。问笼中鸡和兔的数量各是多少?

首先分析一下鸡兔同笼问题。一只鸡有一个头和两只脚,一只兔有一个头和4只脚。这样,如果假定都是鸡,那么每个头对应两只脚。如此推算,35个头对应70只脚,但是这里共有94只脚。因此,剩余的脚就是兔子的另外两只脚。只需将剩余的脚除以2便得到兔子的数量。有了兔子的数量,便可以轻松计算得到鸡的数量。

/**
 * 鸡兔同笼
 * @param head
 * @param foot
 */
public static void JTTL(int head, int foot) {
    int t = (foot - 2 * head) / 2;//兔
    int j = head - t;
    System.out.printf("鸡的数量为%d,兔的数量为%d\n", j, t);
}


public static void main(String[] args) {
    JTTL(35, 94);
}

鸡的数量为23,兔的数量为12

4、猴子吃桃

猴子吃桃问题是一个典型的递归算法的问题。猴子吃桃问题的大意如下:
某天一只猴子摘了一堆桃子,每天吃掉其中的一半然后再多吃一个,第二天则吃剩余的一半然后再多吃一个,…,直到第10天,猴子发现只有1个桃子了。问这只猴子在第一天摘了多少个桃子?

/**
 * 猴子吃桃
 * @param n 天数
 * @return
 */
public static int peach(int n) {
    int p;
    if (n == 1) {
        return 1;//最后一天剩一个
    } else {
        p = (peach(n - 1) + 1) * 2;//前一天总比后一天多一个再两倍
    }
    return p;
}


public static void main(String[] args) {
    System.out.println(peach(10));
}


1534

5、窃贼问题

窃贼问题是一个典型的最优化的问题。窃贼问题的大意如下。
有一个窃贼带着一个背包去偷东西,房屋中共有5件物品,其重量和价值如下。
物品1:6公斤,48元。
物品2:5公斤,40元。
物品3:2公斤,12元。
物品4:1公斤,8元。
物品5:1公斤,7元。
窃贼希望能够拿最大价值的东西,而窃贼的背包最多可装重量为8公斤的物品。那么窃贼应该装下列哪些物品才能达到要求呢?

这是一个经典的 0-1背包问题,我们需要在背包容量为8公斤的限制下,选择物品(每个物品只能选或不选)使得总价值最大。

物品重量价值单位重量价值
16488.00
25408.00
32126.00
4188.00
5177.00

约束条件:

  • 背包容量:8 kg
  • 每个物品要么选要么不选(0-1)
  • 目标:总价值最大

动态规划求解:

//物品信息
static class Item {
    int id;
    int weight;
    int value;
    double valuePerWeight;

    public Item(int id, int weight, int value) {
        this.id = id;
        this.weight = weight;
        this.value = value;
        this.valuePerWeight = (double) value / weight;
    }

    @Override
    public String toString() {
        return String.format("物品%d: %dkg, %d元, 单价: %.2f元/kg",
                id, weight, value, valuePerWeight);
    }
}

/**
 * 结果类
 */
static class Result {
    List<Item> selectedItems;
    int totalValue;
    int totalWeight;
    int capacity;
    String algorithm;

    Result(List<Item> selectedItems, int totalValue, int totalWeight, int capacity) {
        this.selectedItems = selectedItems;
        this.totalValue = totalValue;
        this.totalWeight = totalWeight;
        this.capacity = capacity;
    }

    void setAlgorithm(String algorithm) {
        this.algorithm = algorithm;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        if (algorithm != null) {
            sb.append("算法: ").append(algorithm).append("\n");
        }
        sb.append("选择的物品:\n");
        for (Item item : selectedItems) {
            sb.append("  ").append(item).append("\n");
        }
        sb.append(String.format("总重量: %d/%d kg\n", totalWeight, capacity));
        sb.append(String.format("总价值: %d 元\n", totalValue));
        sb.append(String.format("背包利用率: %.1f%%\n",
                (double) totalWeight / capacity * 100));
        return sb.toString();
    }

    public boolean equals(Result other) {
        if (this.totalValue != other.totalValue) return false;
        if (this.totalWeight != other.totalWeight) return false;
        if (this.selectedItems.size() != other.selectedItems.size()) return false;

        Set<Integer> ids1 = new HashSet<>();
        Set<Integer> ids2 = new HashSet<>();
        for (Item item : selectedItems) ids1.add(item.id);
        for (Item item : other.selectedItems) ids2.add(item.id);

        return ids1.equals(ids2);
    }
}

/**
 * 方法1:动态规划求解0-1背包
 * 时间复杂度:O(n×W)
 */
public static Result solveByDP(Item[] items, int capacity) {
    int n = items.length;//物品数量
    //dp[i][w] 表示前i个物品,容量为w时的最大价值
    int[][] dp = new int[n + 1][capacity + 1];
    //记录选择
    boolean[][] keep = new boolean[n + 1][capacity + 1];
    //动态规划
    for (int i = 1; i <= n; i++) {
        Item item = items[i - 1];
        for (int w = 0; w <= capacity; w++) {
            //不选当前物品
            dp[i][w] = dp[i - 1][w];
            //如果可以选当前物品
            if (w >= item.weight) {
                int newValue = dp[i - 1][w - item.weight] + item.value;
                if (newValue > dp[i][w]) {
                    dp[i][w] = newValue;
                    keep[i][w] = true;
                }
            }
        }
    }
    // 回溯找到选择的物品
    List<Item> selectedItems = new ArrayList<>();
    int remainingCapacity = capacity;
    int totalValue = dp[n][capacity];
    int totalWeight = 0;

    for (int i = n; i > 0; i--) {
        if (keep[i][remainingCapacity]) {
            Item item = items[i - 1];
            selectedItems.add(item);
            remainingCapacity -= item.weight;
            totalWeight += item.weight;
        }
    }

    Collections.reverse(selectedItems);
    return new Result(selectedItems, totalValue, totalWeight, capacity);
}



public static void main(String[] args) {
    System.out.println("========== 0-1背包问题求解 ==========\n");

    // 物品信息
    Item[] items = {
            new Item(1, 6, 48),  // 物品1
            new Item(2, 5, 40),  // 物品2
            new Item(3, 2, 12),  // 物品3
            new Item(4, 1, 8),   // 物品4
            new Item(5, 1, 7)    // 物品5
    };

    int capacity = 8;  // 背包容量

    System.out.println("问题描述:");
    System.out.println("背包容量: " + capacity + " kg");
    System.out.println("可用物品:");
    for (Item item : items) {
        System.out.println("  " + item);
    }
    System.out.println();

    // 动态规划求解
    System.out.println("1. 动态规划求解:");
    Result dpResult = solveByDP(items, capacity);
    dpResult.setAlgorithm("动态规划");
    System.out.println(dpResult);
}


算法: 动态规划
选择的物品:
  物品1: 6kg, 48, 单价: 8.00/kg
  物品4: 1kg, 8, 单价: 8.00/kg
  物品5: 1kg, 7, 单价: 7.00/kg
总重量: 8/8 kg
总价值: 63 元
背包利用率: 100.0%
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值