引用一下习题的原文链接:https://blog.csdn.net/obession/article/details/68951351
大致问题就是:
小y有一个宽度为 100cm,高度为 20cm,深度为 1cm 的柜子,还有 36个礼物盒,他们的深度都为 1cm,有对应的宽度和高度。单位(cm)。
现在想把36件礼物盒子放到柜子上,由于礼物盒里面都装有礼物,礼物盒不堆放的放置。由于礼物盒深度和柜子深度一样,所以礼物盒和柜子深度方向也必须一致。并且礼物盒的高度不能大于柜子的高度,否者放不进去。小y希望放到柜子上礼物盒的宽度和正好等于柜子的宽度,也就是希望柜子两边都不存在间隙。在满足条件的情况下,小y 希望能尽可能多的放置礼物盒,算出最多能放多少个礼物盒。
开始解题:
收到问题当时第一反应写的代码思路 填入数据后删除不合规则的物品 代码如下
(不用这个方法,特别笨!!!! )
// 更新物品list
public static void updateList(List<Integer> listWide, List<Integer> listHigh, String[] text) {
for (int i = 0; i < text.length; i++) {
if (i % 2 == 0) // 偶数为宽
{
listWide.add(Integer.parseInt(text[i]));
} else {
listHigh.add(Integer.parseInt(text[i]));
}
}
}
// 记录物品高大于20的索引
public static void recordIndex(List<Integer> list, List<Integer> number) {
int num = 0;// 记录进行中的索引
// 记录高大于20的索引
for (Integer str : list) {
if (str > 20) {
number.add(num);
}
num++;
}
}
// 删除高度>20对应索引的物品
public static void removeList(List<Integer> list, List<Integer> number) {
Iterator<Integer> iter = list.iterator();
while (iter.hasNext()) {
int item = iter.next();
for (int i = 0; i < number.size(); i++) {
if (item == number.indexOf(i)) {
iter.remove();
}
}
}
}
// 更新满足条件物品的宽高list
public static void update(List<Integer> listWide, List<Integer> listHigh, List<Integer> number, String[] text) {
updateList(listWide, listHigh, text);
recordIndex(listHigh, number);
removeList(listWide, number);
removeList(listHigh, number);
}
public static void main(String[] args) {
// 利用split直接将复制的元素装入text数组节省输入数据的时间 2i是宽 2i+1是高
String[] text = "11 3 8 12 11 17 16 13 1 14 2 8 6 10 10 18 17 11 10 15 6 14 5 6 2 19 19 10 4 9 7 9 5 14 5 20 15 19 3 17 15 11 7 25 11 20 9 12 17 4 9 19 4 18 10 10 12 19 17 3 19 9 20 16 11 16 10 2 20 15 3 14".split(" ");
// 筛选高度>20的排除 map集合更适合此操作 但为了保持有序(通过删除不合规则的高度确定删除宽度的索引)使用list 为优化时间复杂度 牺牲空间
List<Integer> listWide = new ArrayList<>();// 创建宽的列表
List<Integer> listHigh = new ArrayList<>();// 创建高的列表
List<Integer> number = new ArrayList<>();// 创建高大于20的索引列表
update(listWide, listHigh, number, text);// 删除不满足条件的物品更新物品列表
// 排序
// 计算
}
优化上面逻辑问题
但是感觉思路好像有问题,一个排序题怎么弄的那么麻烦,后来突然想起来我不应该添加之后在筛选,应该筛选之后在添加,这样不仅能省去list删除不合规则的元素时记录索引和执行删除元素的时间,还能省去两个集合的内存和代码空间。于是将填入数据后删除不合规则的数据逻辑更改为只添加符合规则的数据,结合实际问题,我们只需要比较符合规则的物品宽度,于是2.0出现了。
// 更新物品list
public static void updateList(List<Integer> listWide, String[] text) {
for (int i = 0; i < text.length; i++) {
if (i % 2 == 0) {
// 加一个判断高大于20的方法,将满足的添加到物品集合
if (Integer.parseInt(text[i + 1]) <= 20) {
listWide.add(Integer.parseInt(text[i]));
}
}
}
}
public static void main(String[] args) {
// 利用split直接将复制的元素装入text数组节省输入数据的时间 2i是宽 2i+1是高
String[] text = "11 3 8 12 11 17 16 13 1 14 2 8 6 10 10 18 17 11 10 15 6 14 5 6 2 19 19 10 4 9 7 9 5 14 5 20 15 19 3 17 15 11 7 25 11 20 9 12 17 4 9 19 4 18 10 10 12 19 17 3 19 9 20 16 11 16 10 2 20 15 3 14"
.split(" ");
// 筛选高度>20的排除 map集合更适合此操作 但为了保持有序(通过删除不合规则的高度确定删除宽度的索引)使用list 为优化时间复杂度 牺牲空间
List<Integer> listWide = new ArrayList<>();// 创建宽的列表
updateList(listWide,text);//将满足物品的装入list
// 排序 采用直接插入排序
// 计算
}
编写排序,考虑时间复杂度的问题原先准备快速排序,但由于数据太少用直接插入排序时间可能比快速排序所需时间更短。因为插入排序属于插入类型的排序,而快速排序属于交换类排序,数据量少时交换所消耗的资源占比大。于是便选择直接插入排序,当数据量大时在采用快速排序。又因为list集合不适合排序于是我们将list集合转为数组,调整逻辑。
// list赋值给int数组
public static int[] transform(List<Integer> listWide) {
int[] wide = new int[listWide.size()];
for (int i = 0; i < listWide.size(); i++) {
wide[i] = listWide.get(i);
}
return wide;
}
// 直接插入排序
public static void insertSort(int[] listWide) {
int[] wide = Arrays.copyOf(listWide, listWide.length);
// 从1开始遍历插入
for (int i = 1; i < wide.length; i++) {
int cup = wide[i]; // 记录要插入的数据
int j = i;// 从排序后的序列最右侧开始比较,将小的放在左侧
while (j > 0 && cup < wide[j - 1]) {
wide[j] = wide[j - 1];
j--;
}
// 索引不相等插入,相等停止插入
if (j != i) {
wide[j] = cup;
}
}
count(wide);// 调用计算方法
}
进行分两种情况,第一种最好情况现在宽度之和减去前面的一件刚好为100,例:现在1+2+2+3+4为10的删去2就行;第二种情况现在宽度减去前面2小件加上后面1大件刚好为100,例1+3+4+4+5后有7找和为15的,删去前面4,5加上后面7就行(理解成前面有两个数的和大于后面一个数的和,排序后和第一种情况一致)。
// 计算
public static void count(int[] listWide) {
int sum = 0;
for (int i = 0; i < listWide.length; i++) {
if (sum < 100) {
sum += listWide[i];
} else {
int index = i - 1;// 记录不满足时的索引
int s = 0;
for (int j = index - 1; j > 0; j--) {
// 第一种可能 减去前面件数就为100 最好情况
if (sum - listWide[j] <= 100) {
s = sum - listWide[j];
if (s == 100) {
System.out.println("宽度和为" + s + "的礼物件数为:" + index + "件!");
break;
}
}
// 第二种 减去前面2件数加后面件数为100 最坏情况
for (int k = index - 1; k < listWide.length; k++) {
for (int l = index - 1; l > 0; l--) {
if (s + listWide[k] - listWide[l] == 100) {
System.out.println("宽度和为" + (s + listWide[k] - listWide[l])+ "的礼物件数为:" + index + "件!");
break;
}
break;
}
}
}
break;
}
}
对代码进行测试,调用函数。
public static void main(String[] args) {
// 利用split直接将复制的元素装入text数组节省输入数据的时间 2i是宽 2i+1是高
String[] text = "11 3 8 12 11 17 16 13 1 14 2 8 6 10 10 18 17 11 10 15 6 14 5 6 2 19 19 10 4 9 7 9 5 14 5 20 15 19 3 17 15 11 7 25 11 20 9 12 17 4 9 19 4 18 10 10 12 19 17 3 19 9 20 16 11 16 10 2 20 15 3 14"
.split(" ");
// 筛选高度>20的排除 map集合更适合此操作 但为了保持有序(通过删除不合规则的高度确定删除宽度的索引)使用list
List<Integer> listWide = new ArrayList<>();// 创建宽的列表
updateList(listWide, text);// 将满足物品的装入list
insertSort(transform(listWide));// 采用直接插入排序(数据量小直接插入排序快) transform()是将list赋值给int数组的方法(数组比集合方便排序)
}
完整代码为代码块2-5,注意代码块2中的main方法更新为代码块5。
摸鱼日记
这道题是昨天晚上室友参加蓝桥杯比赛的辅导老师发的训练题目,本来窝当时买了瓶超大瓶的快乐水准备追番的,然后寝室讨论了这道题我也就参与进去了,由于平常上课时没有认真听课,可能导致实现方法有点低级,只能勉强解题,多多去掌握其他方法,加油,今天就先学到这,窝继续去摸鱼。
2021.11.22 晴