目录
最优装载问题
问题描述
海盗船载重量为c,每件古董载重量为 w i w_i wi,海盗要怎样才能把尽可能多的宝物装上船?
Java实现
import java.util.Arrays;
import java.util.Scanner;
public class BestLoadingProblem {
public static void main(String[] args) {
double[] w;
double c;
int n;
Scanner scanner = new Scanner(System.in);
System.out.print("请输入载重量c及古董个数n:");
c = scanner.nextDouble();
n = scanner.nextInt();
w = new double[n];
System.out.print("请输入每个古董的质量,用空格分开:");
for (int i = 0; i < n; i++) {
w[i] = scanner.nextDouble();
}
Arrays.sort(w);
double tmp = 0.0;
int ans = 0;
for (int i = 0; i < n; i++) {
tmp += w[i];
if (tmp <= c) {
ans++;
} else {
break;
}
}
System.out.println("能装载的古董最大数量为Ans="+ans);
}
}
注:Java中数组的排序可以通过调用
Arrays
类的sort
函数来执行。
背包问题
问题描述
假设山洞中有n种宝物,每种宝物有一定重量w和响应的价值v,宝物可以分割。毛驴的运载能力为m,怎样才能使毛驴运走的宝物价值更大?
Java实现
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class KnapsackProblem {
static class Treasure {
public Treasure(double w, double v) {
this.w = w;
this.v = v;
this.p = v / w;
}
double w;
double v;
double p;
}
public static void main(String[] args) {
int n;
double m;
Scanner scanner = new Scanner(System.in);
System.out.print("请输入宝物数量n及毛驴载重量m:");
n = scanner.nextInt();
m = scanner.nextDouble();
Treasure[] treasures = new Treasure[n];
System.out.print("请输入每个宝物的重量和价值,用空格分开:");
for (int i = 0; i < n; i++) {
treasures[i] = new Treasure(scanner.nextDouble(), scanner.nextDouble());
}
Arrays.sort(treasures, new Comparator<Treasure>() {
@Override
public int compare(Treasure o1, Treasure o2) {
double difference = o2.p - o1.p;
if (difference > 0.001) {
return 1;
} else if (difference < -0.001) {
return -1;
} else {
return 0;
}
}
});
double sum = 0.0;
for (int i = 0; i < n; i++) {
Treasure treasure = treasures[i];
if (m > treasure.w) {
m -= treasure.w;
sum += treasure.v;
} else {
sum += m * treasure.p;
break;
}
}
System.out.println("能装载的宝物的最大价值Maximum value=" + sum);
}
}
注:
- 为了更简单便捷地定义宝物的数据结构,这里使用了静态内部类的语法,静态内部类和外部类相对独立,不用实例化外部类即可使用。
LinkedList
的源码中就是用静态内部类Node
来储存链表中的每个节点。- 自定义对象数组无法直接调用
Arrays
类的sort
函数进行排序,常见的解决方法是让自定义对象实现Comparable
接口或者调用需要传入一个Comparator
对象作为参数的sort
函数,这里选用的是后者。Comparator
是泛型接口,里面的compare
函数实现了对泛型对象的比较。compare
函数返回整数,正数、0、负数分别代表前一个参数的对象大于、等于、小于后一个参数的对象。因为sort函数是升序排序,所以要调换比较的顺序。- 因为浮点数的精度问题,这里选择了一种常见的利用差值的比较办法,差值的绝对值在指定范围内则可以近似认为两个浮点数相等。
- 为了简化代码,在给
sort
函数传入Comparator
对象时使用了匿名类的语法。匿名类实现了一种不需要提供任何的类名直接实例化内部类的语法。
会议安排问题
问题描述
有n个会议,每个会议都有开始时间和结束时间,如何安排会议才能尽可能在有限的时间内召开更多的会议?
Java实现
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class MeetingArrangementProblem {
static class Meet {
int begin;
int end;
int num;
public Meet(int begin, int end, int num) {
this.begin = begin;
this.end = end;
this.num = num;
}
}
private int n, ans;
private Meet[] meets;
public void init() {
int s, e;
Scanner scanner = new Scanner(System.in);
System.out.print("请输入会议总数n:");
this.n = scanner.nextInt();
this.meets = new Meet[n];
System.out.println("请输入每个会议的开始时间和结束时间,用空格分开:");
for (int i = 0; i < n; i++) {
s = scanner.nextInt();
e = scanner.nextInt();
meets[i] = new Meet(s, e, i + 1);
}
}
public void solve() {
Arrays.sort(meets, new Comparator<Meet>() {
@Override
public int compare(Meet o1, Meet o2) {
if (o1.end == o2.end) {
return o2.begin - o1.begin;
} else {
return o1.end - o2.end;
}
}
});
System.out.println("排完序的会议时间如下:");
int i;
System.out.printf("%-8s %-7s %-7s%n", "会议编号", "开始时间", "结束时间");
for (i = 0; i < n; i++) {
Meet meet = meets[i];
System.out.printf("%-10d %-10d %-10d%n", meet.num, meet.begin, meet.end);
}
System.out.println("-----------------------------");
System.out.println("选择会议的过程:");
System.out.println("\t选择第" + meets[0].num + "个会议");
ans = 1;
int last = meets[0].end;
for (i = 1; i < n; i++) {
Meet meet = meets[i];
if (meet.begin >= last) {
ans++;
last = meet.end;
System.out.println("\t选择第" + meet.num + "个会议");
}
}
System.out.println("最多可以安排"+ans+"个会议");
}
public static void main(String[] args) {
MeetingArrangementProblem problem = new MeetingArrangementProblem();
problem.init();
problem.solve();
}
}
注:关于
sort
函数,因为设计上应该是按照结束时间升序排列,结束时间相同按照开始时间降序排列,才有了上面的Compare
函数。
最短路径问题
问题描述
有n个城市,其中两两连接的路径有m条,问从一点出发到其他个点的最短路径是多少?
Java实现
import java.util.Arrays;
import java.util.Scanner;
import java.util.Stack;
public class ShortestPathProblem {
private static int n, m;
private static int[][] map;
private static int[] dist;
private static int[] p;
private static boolean[] flag;
private static void dijkstra(int u) {
for (int i = 1; i <= n; i++) {
dist[i] = map[u][i];
flag[i] = false;
if (dist[i] == Integer.MAX_VALUE) {
p[i] = -1;
} else {
p[i] = u;
}
}
dist[u] = 0;
flag[u] = true;
for (int i = 1; i <= n; i++) {
int temp = Integer.MAX_VALUE, t = u;
for (int j = 1; j <= n; j++) {
if (!flag[j] && dist[j] < temp) {
t = j;
temp = dist[j];
}
}
if (t == u) {
return;
}
flag[t] = true;
for (int j = 1; j <= n; j++) {
if (!flag[j] && map[t][j] < Integer.MAX_VALUE) {
if (dist[j] > (dist[t] + map[t][j])) {
dist[j] = dist[t] + map[t][j];
p[j] = t;
}
}
}
}
}
public static void main(String[] args) {
int u, v, w, st;
Scanner scanner = new Scanner(System.in);
System.out.print("请输入城市的个数:");
n = scanner.nextInt();
map = new int[n + 1][n + 1];
dist = new int[n + 1];
p = new int[n + 1];
flag = new boolean[n + 1];
System.out.print("请输入城市之间路线的个数:");
m = scanner.nextInt();
System.out.println("请输入城市u,v之间的路线以及距离w:");
for (int i = 1; i <= n; i++) {
Arrays.fill(map[i], Integer.MAX_VALUE);
}
for (; m > 0; m--) {
u = scanner.nextInt();
v = scanner.nextInt();
w = scanner.nextInt();
map[u][v] = w;
}
System.out.print("请输入小明所在的位置:");
st = scanner.nextInt();
dijkstra(st);
System.out.println("小明所在的位置:" + st);
for (int i = 1; i <= n; i++) {
System.out.print("小明:" + st + " -> " + i);
if (dist[i] == Integer.MAX_VALUE) {
System.out.println(" 没有路径可以抵达");
} else {
System.out.println(" 最短距离:" + dist[i]);
}
}
findPath(st);
}
public static void findPath(int u) {
int x;
Stack<Integer> s = new Stack<>();
System.out.println("源点为:" + u);
for (int i = 1; i <= n; i++) {
x = p[i];
if (x == -1 && i != u) {
System.out.println("源点没有通往" + i + "的路径");
continue;
}
while (x != -1) {
s.push(x);
x = p[x];
}
System.out.print("源点到" + i + "的最短路径为:");
while (!s.isEmpty()) {
System.out.print(s.pop() + "->");
}
System.out.println(i+" 最短距离为:" + dist[i]);
}
}
}
哈夫曼编码问题
问题描述
输入n个字符和其出现的频率,如何为每一个字符分配一个哈夫曼编码?
Java实现
import java.util.ArrayList;
import java.util.Objects;
import java.util.Scanner;
public class HuffmanCodingProblem {
private final ArrayList<Node> nodeList = new ArrayList<>();
private Node huffmanTree;
static class Node {
Node parent;
Node leftChild;
Node rightChild;
final double weight;
String huffmanCode;
final char charValue;
public Node(char charValue, double weight) {
this.weight = weight;
this.charValue = charValue;
}
}
public static void main(String[] args) {
HuffmanCodingProblem problem = new HuffmanCodingProblem();
problem.getInput();
problem.solve();
}
private void solve() {
createHuffmanTree();
generateHuffmanCode();
printHuffmanCode();
}
private void printHuffmanCode() {
for (Node node:nodeList){
System.out.println(node.charValue+": Huffman code is: "+node.huffmanCode);
}
}
private void generateHuffmanCode() {
recursiveGenerateHuffmanCode(huffmanTree, "");
}
private void recursiveGenerateHuffmanCode(Node root, String code) {
if (root == null) {
return;
}
root.huffmanCode = code;
recursiveGenerateHuffmanCode(root.leftChild, code + "0");
recursiveGenerateHuffmanCode(root.rightChild, code + "1");
}
private void createHuffmanTree() {
ArrayList<Node> tempList = new ArrayList<>(nodeList);
int cnt = 0;
while (tempList.size() != 1) {
cnt++;
Node left = null, right = null;
double weight1, weight2;
weight1 = weight2 = Double.MAX_VALUE;
for (Node node : tempList) {
if (node.weight < weight1 && node.parent == null) {
if (left != null) {
right = left;
weight2 = right.weight;
}
left = node;
weight1 = node.weight;
} else if (node.weight < weight2 && node.parent == null) {
right = node;
weight2 = node.weight;
}
}
System.out.println("第" + cnt + "次选择 左右节点的权值分别为 " + weight1 + " " + weight2);
Node parent = new Node('\0', weight1 + weight2);
parent.leftChild = left;
parent.rightChild = right;
Objects.requireNonNull(left).parent = parent;
Objects.requireNonNull(right).parent = parent;
tempList.add(parent);
tempList.remove(left);
tempList.remove(right);
}
huffmanTree = tempList.get(0);
}
private void getInput() {
Scanner scanner = new Scanner(System.in);
System.out.println("Please input n:");
int n = scanner.nextInt();
for (int i = 0; i < n; i++) {
System.out.println("Please input value and weight of leaf node " + (i + 1));
Node node = new Node(scanner.next().charAt(0), scanner.nextDouble());
nodeList.add(node);
}
}
}
最小生成树问题
问题描述
已知n个节点m条边的无向连通带权图,求基于某节点i的该图的最小生成树?
Java实现
Prim算法
import java.util.Scanner;
public class MinimumSpanningTreeProblemPrimVer {
private int n;
private int u0;
private int[][] c;
private int[] lowCost, closest;
private boolean[] s;
private void solve(){
System.out.println("数组lowCost的内容为:");
int sumCost = 0;
for(int i=1;i<=n;i++){
sumCost+=lowCost[i];
System.out.print(lowCost[i]+" ");
}
System.out.println("\n最小的花费是:"+sumCost);
}
private void generateByPrim() {
s[u0] = true;
int i, j;
for (i = 1; i <= n; i++) {
if (i != u0) {
lowCost[i] = c[u0][i];
closest[i] = u0;
s[i] = false;
} else {
lowCost[i] = 0;
}
}
for (i = 1; i <= n; i++) {
int temp = Integer.MAX_VALUE;
int t = u0;
for (j = 1; j <= n; j++) {
if ((!s[j]) && (lowCost[j] < temp)) {
t = j;
temp = lowCost[j];
}
}
if (t == u0) {
break;
}
s[t] = true;
for (j = 1; j <= n; j++) {
if ((!s[j]) && (c[t][j] < lowCost[j])) {
lowCost[j] = c[t][j];
closest[j] = t;
}
}
}
}
private void getInput() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入节点数n和边数m:");
n = scanner.nextInt();
c = new int[n + 1][n + 1];
s = new boolean[n + 1];
closest = new int[n + 1];
lowCost = new int[n + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
c[i][j] = Integer.MAX_VALUE;
}
}
int m = scanner.nextInt();
System.out.print("请输入节点数u,v和边的权值w:");
int u, v, w;
for (int i = 1; i <= m; i++) {
u = scanner.nextInt();
v = scanner.nextInt();
w = scanner.nextInt();
c[u][v] = c[v][u] = w;
}
System.out.print("请输入任意节点u0:");
u0 = scanner.nextInt();
}
public static void main(String[] args) {
MinimumSpanningTreeProblemPrimVer problem = new MinimumSpanningTreeProblemPrimVer();
problem.getInput();
problem.generateByPrim();
problem.solve();
}
}
Kruskal算法
public class MinimumSpanningTreeProblemKruskalVerVer {
private int n;
private int u0;
private Set<Set<Integer>> nodeSet = new HashSet<>();
private List<Edge> edgeList = new ArrayList<>();
private List<Edge> minimumEdge = new ArrayList<>();
static class Edge {
int point1, point2;
int weight;
public Edge(int point1, int point2, int weight) {
this.point1 = point1;
this.point2 = point2;
this.weight = weight;
}
}
private void solve() {
System.out.println("最小生成树的边分别为:");
int sumCost = 0;
for (Edge edge : minimumEdge) {
System.out.println(edge.point1 + "号节点 " + edge.point2 + "号节点 " + edge.weight);
sumCost+=edge.weight;
}
System.out.println("\n最小的花费是:" + sumCost);
}
private void generateByKruskal() {
for (int i = 1, j = 0; i <= n - 1; i++) {
Edge edge = edgeList.get(j);
boolean flag = false;
Set<Integer> set1 = null, set2 = null;
for (Set<Integer> set : nodeSet) {
if (set.contains(edge.point1)) {
set1 = set;
}
if (set.contains(edge.point2)) {
set2 = set;
}
if (set1 != null && set1 == set2) {
flag = true;
break;
}
}
if (flag) {
j++;
i--;
} else {
set1.addAll(set2);
minimumEdge.add(edge);
nodeSet.remove(set2);
}
}
}
private void getInput() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入节点数n和边数m:");
n = scanner.nextInt();
for (int i = 1; i <= n; i++) {
Set<Integer> set = new HashSet<>();
set.add(i);
nodeSet.add(set);
}
int m = scanner.nextInt();
System.out.print("请输入节点数u,v和边的权值w:");
int u, v, w;
for (int i = 1; i <= m; i++) {
u = scanner.nextInt();
v = scanner.nextInt();
w = scanner.nextInt();
Edge edge = new Edge(u, v, w);
edgeList.add(edge);
}
edgeList.sort(new Comparator<Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
});
System.out.print("请输入任意节点u0:");
u0 = scanner.nextInt();
}
public static void main(String[] args) {
MinimumSpanningTreeProblemKruskalVerVer problem = new MinimumSpanningTreeProblemKruskalVerVer();
problem.getInput();
problem.generateByKruskal();
problem.solve();
}
}