一、放灯问题
给定一个字符串str,只由‘X’和‘.’两种字符构成。
‘X’表示墙,不能放灯,也不需要点亮
‘.’表示居民点,可以放灯,需要点亮
如果灯放在i位置,可以让i-1,i和i+1三个位置被点亮
返回如果点亮str中所有需要点亮的位置,至少需要几盏灯。
贪心策略:
分为两种情况
i为当前字符处
第一种情况:当前字符是墙 ’ X ',这种情况下下次决定直接跳到下一个 i+1 字符处。
第二种情况:当前字符是居民点 ’ . ',这种情况下先看下一个字符,如果 i+1 字符是 ‘ X ’,则下次决定直接到 i+2 字符处,如果 i+1 字符是 ’ . ',则下次决定跳到 i+3 字符处。
这样局部的每一步都寻求到最优的决定,最后得到全局最优的决定。
代码如下:
public static int minLight2(String road){
char[] str=road.toCharArray();
int i=0;
int light=0;
while (i<str.length) {
//当前字符为‘X’,下一次决定到i+1字符处
if (str[i]=='X') {
i=i+1;
}else {
//不管i到i+3中都是什么字符,都要有一盏灯
light++;
if (i+1==str.length){
break;
}
else {
//如果i+1字符处为‘X’,下次决定直接跳到i+2
if (str[i+1]=='X'){
i=i+2;
//如果i+1字符处为‘.’,下次决定直接跳到i+3
}else{
i=i+3;
}
}
}
}
return light;
}
二、分割金条
一块金条切成两半,是需要花费和长度数值一样的铜板的。
比如长度为20的金条,不管切成长度多大的两半,都要花费20个铜板。一群人想整分整块金条,怎么分最省铜板?
例如,给定数组{10,20,30},代表一共三个人,整块金条长度为 10+20+30=60. 金条要分成10,20,30三个部分。
如果, 先把长度60的金条分成10和50,花费60 再把长度50的金条分成20和30, 花费50 一共花费110铜板。
但是如果, 先把长度60的金条分成30和30,花费60 再把长度30 金条分成10和20,花费30 一共花费90铜板。
输入一个数组,返回分割的最小代价。(不考虑给定的数组顺序,只要能切出数组中的长度即可)
贪心策略:
- 准备一个小根堆,把数组中的数据放入小根堆当中
- 从小根堆依次弹出两个数(每次都是弹出小根堆中最小的两个数),相加,再放入小根堆
- 依次弹出的数相加生成哈夫曼树,哈夫曼树带权路径的权值之和就是题目中分割的最小代价
哈夫曼树:给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
代码如下:
public static int lessMoney2(int[] arr) {
//小根堆
PriorityQueue<Integer> pQ=new PriorityQueue<>();
//将数组中数依次加入小根堆
for (int i=0;i<arr.length;i++) {
pQ.add(arr[i]);
}
int sum=0;
int cur=0;
//弹出相加,放回小根堆
while (pQ.size()>1) {
cur=pQ.poll()+pQ.poll();
sum+=cur;
pQ.add(cur);
}
return sum;
}
三、项目获取最大钱数
输入:正数数组costs、正数数组profits、正数K、正数M
给你两个数组,costs[i]和profit[i]是,costs[i]表示每个项目所花费的钱数,profit[i]表示每个项目所能挣得的利润。
k表示你不能并行、只能串行的最多做k个项目
w表示你初始的资金
每个项目只做一次
说明:你每做完一个项目,马上获得的收益,可以支持你去做下 一个 项目。
输出: 你最后获得的最大钱数。
贪心策略:
- 准备一个大根堆和一个小根堆,大根堆中根据项目的利润排序,小根堆中根据项目的花费排序。
- 将项目放入小根堆中排序
- 把小根堆中花费小于等于初始资金的项目放入大根堆中。
- 依次弹出大根堆,每次都是弹出大根堆中利润最大的值
// 最多K个项目
// W是初始资金
// Profits[] 利润
// Capital[] 资金
// 一定等长
// 返回最终最大的资金
public static int findMaximizedCapital(int K, int W, int[] profits, int[] Capital) {
//小根堆
PriorityQueue<Program> minCostQ = new PriorityQueue<>(new MinCostComparator());
//大根堆
PriorityQueue<Program> maxProfitQ = new PriorityQueue<>(new MaxProfitComparator());
//将项目放入小根堆中
for (int i = 0; i < profits.length; i++) {
minCostQ.add(new Program(profits[i], Capital[i]));
}
//只能进行K个项目
for (int i = 0; i < K; i++) {
//小根堆中花费小于初始资金的项目弹出放入大根堆中
while (!minCostQ.isEmpty() && minCostQ.peek().c <= W) {
maxProfitQ.add(minCostQ.poll());
}
if (maxProfitQ.isEmpty()) {
return W;
}
//利润求和
W += maxProfitQ.poll().p;
}
return W;
}
//项目类
public static class Program {
public int p;
public int c;
public Program(int p, int c) {
this.p = p;
this.c = c;
}
}
//小根堆比较器,根据项目的花费排序
public static class MinCostComparator implements Comparator<Program> {
@Override
public int compare(Program o1, Program o2) {
return o1.c - o2.c;
}
}
//大根堆比较器,根据项目的利润排序
public static class MaxProfitComparator implements Comparator<Program> {
@Override
public int compare(Program o1, Program o2) {
return o2.p - o1.p;
}
}
public static void main(String[] args) {
int[] a = {
0, 1, 1};
int[] b = {
1, 2, 3};
System.out.println(findMaximizedCapital(2, 0, b, a));
}