木棍加工 - 贪心

167 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

一堆木头棍子共有n根,每根棍子的长度和宽度都是已知的。棍子可以被一台机器一个接一个地加工。机器处理一根棍子之前需要准备时间。准备时间是这样定义的:

第一根棍子的准备时间为1分钟;

如果刚处理完长度为L,宽度为W的棍子,那么如果下一个棍子长度为Li,宽度为Wi,并且满足L>=Li,W>=Wi,这个棍子就不需要准备时间,否则需要1分钟的准备时间;

计算处理完n根棍子所需要的最短准备时间。比如,你有5根棍子,长度和宽度分别为(4, 9),(5, 2),(2, 1),(3, 5),(1, 4),最短准备时间为2(按(4, 9)、(3, 5)、(1, 4)、(5, 2)、(2, 1)的次序进行加工)。

来源:洛谷 www.luogu.com.cn/problem/P12…

输入格式

第一行是一个整数n(n<=5000),第2行是2n个整数,分别是L1,W1,L2,w2,…,Ln,Wn。L和W的值均不超过10000,相邻两数之间用空格分开。

5
4 9 5 2 2 1 3 5 1 4

输出格式

仅一行,一个整数,所需要的最短准备时间。

2

二、思路分析:

这道题目想清楚后超级简单, 其实它就是一个套娃(大的里面套小的)

依题目所述,我们只要每次找到还没有被加工的木棍中长和宽最大的木棍,再去找剩下的木棍中长和宽都小于等于它的木棍进行加工就不用花费时间(L>=Li,W>=Wi),但是当 A棍的长和宽 > B棍的长和宽 > C棍的长和宽时,要是我们先加工C, 漏掉B的话,就会多花费时间。

贪心算法的特点是以当前情况为基础根据某个优化测度作最优选择,而不考虑各种可能的整体情况,获得局部最优解。我们这道题的贪心就在于将木棍根据长度最长/宽度最宽进行排序后,每次花费1分钟准备时间的木棍一定是不能被免费加工的,然后按排序顺序去查有没有能比该木棍宽、窄免费加工的,并不断刷新限制长和宽。

下面的代码是根据木棍长度排序的,如果长度相同,就根据宽度排序,这样能保证前面的木棍不能在后面的木棍之后免费加工,visit记录木棍有没有被加工,被加工了就跳过

三、AC 代码:


import java.io.*;
import java.util.*;

public class Main {
	static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

	public static void main(String[] args) {
		int n = getInt();
		stick[] s = new stick[n];
		for (int i = 0; i < n; i++) {
			s[i] = new stick(getInt(), getInt());
		}
           // 比较器
		Comparator<stick> c = new Comparator<stick>() {
			@Override
			public int compare(stick o1, stick o2) {
				if (o2.L == o1.L)
					return o2.W - o1.W;
				return o2.L - o1.L;
			}
		};
		Arrays.sort(s, c);

		boolean[] visit = new boolean[n];
           // time 记录准备时间
		int time = 0;
           // 遍历排序后的木棍
		for (int i = 0; i < n; i++) {
			if (visit[i] == false) {
				time++;
				int w = s[i].W;
				for (int j = i + 1; j < n; j++) {
                          // 满足免费加工的木棍,就改变该木棍的状态,并将w改掉,方便下一次判断是否能免费加工, 长就不用了,毕竟我们是按长排序的
					if (visit[j] == false && s[j].W <= w) {
						visit[j] = true;
						w = s[j].W;
					}
				}
			}
		}
		System.out.println(time);

	}

	private static int getInt() {
		try {
			in.nextToken();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return (int) in.nval;
	}

}
class stick {
     // L (long)长   W(width) 宽
	int L;
	int W;

	public stick(int l, int w) {
		L = l;
		W = w;
	}

}

四、总结:

贪心是一个很神奇的算法,就是你会用,但是你不知道该在什么时候用😶 要确定一个问题是否适合用贪心算法求解,必须证明每一步所作的贪心选择最终导致问题的整体最优解。