贪心法
贪心法是一类求取最优化问题的办法,贪心法在遵循某种规则的情况下对当前状态进行判断,每次在进行状态转换的情况时选取其中能使当前状况获得最大收益的办法(当前状况下的局部最优),最终通过局部最优来取得全局最优解的办法。
以下列出几个例子,演示贪心法通常的应用情况:
- (1020 月饼 (25分) ----- 求解最大收益类问题
月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。
注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如我们有 3 种月饼,其库存量分别为 18、15、10 万吨,总售价分别为 75、72、45 亿元。如果市场的最大需求量只有 20 万吨,那么我们最大收益策略应该是卖出全部 15 万吨第 2 种月饼、以及 5 万吨第 3 种月饼,获得 72 + 45/2 = 94.5(亿元)。
根据题意可以知道贪心法在此问题中遵循的规则是 使得销售获取的收益最大,进一步说就是每次选取时都选取单价售出价格最高的物品。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;
import java.util.Comparator;
class pair {
public double w; // 重量
public double v; // 价格
public double uintV;
}
public class Main {
public static void main(String[] args) throws IOException {
// 数据输入模块
int n, W;
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
n = (int)in.nval;
in.nextToken();
W = (int)in.nval;
pair[] data = new pair[n];
for (int i = 0; i < n; i++) {
data[i] = new pair();
in.nextToken();
data[i].w = in.nval;
}
for(int i = 0;i < n;i++) {
in.nextToken();
data[i].v = in.nval;
data[i].uintV = data[i].v / data[i].w;
}
// 数据预处理
Arrays.sort(data, new comparator());
double Sum = 0;
// 计算当前最大收益
for(int i = 0;i < n && W> 0;i++) {
double temp = Math.min(W, data[i].w);
W -= temp;
Sum +=data[i].uintV * temp;
}
System.out.printf("%.2f",Sum);
}
}
class comparator implements Comparator<pair> {
@Override
public int compare(pair o1, pair o2) {
// TODO Auto-generated method stub
if(o1.uintV - o2.uintV > 0) {
return -1;
} else {
return 1;
}
}
}
该类问题通常都有很明确的划分点,可以保证下一步状态转换是局部最优的。
2.哈夫曼树 ----- 俩俩合并类问题/合并果子问题 例题为Poj-3253
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.PriorityQueue;
import java.util.Queue;
public class Main {
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
int n = (int)in.nval;
Queue<Integer> queue = new PriorityQueue<Integer>();
// 数据预处理
for(int i = 0;i < n;i++) {
in.nextToken();
int temp = (int)in.nval;
queue.offer(temp);
}
// 求最小加权路径
long ans = 0;
while(queue.size() != 1) {
Integer a = queue.poll();
Integer b = queue.poll();
ans += a + b;
queue.offer(a+b);
}
System.out.println(ans);
}
}
此类问题需要对二叉树有一定了解,本质上就是求树的最小加权路径。
这里我们采用优先队列的数据类性进行辅助处理,可以更容易的获取解。
3 区间类问题
问题通常为多个线段映射到一条直线上,求解如何选择线段使得线段覆盖率最大或者线段最多问题。