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("在指定范围内未找到解");
}
}
线性方程组求解 (H ≤ 5000):
找到解: 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公斤的限制下,选择物品(每个物品只能选或不选)使得总价值最大。
| 物品 | 重量 | 价值 | 单位重量价值 |
|---|---|---|---|
| 1 | 6 | 48 | 8.00 |
| 2 | 5 | 40 | 8.00 |
| 3 | 2 | 12 | 6.00 |
| 4 | 1 | 8 | 8.00 |
| 5 | 1 | 7 | 7.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%

275

被折叠的 条评论
为什么被折叠?



