贪心算法
概念:贪心算法又叫做贪婪算法,它在求解某个问题是,总是做出眼前最大利益。
也就是说只顾眼前不顾大局,所以它是局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
贪心算法其最重要的两个点就是:
- 贪心策略
- 通过局部最优解能够得到全局最优解
会议室安排问题:
有N个同等级的会议需要在同一天使用同一个会议室,现在给你这个N个会议的开始和结束时间,你怎么样才能使会议室最大利用?即安排最多场次的会议。
例如有以下时间的会议
0点~9点
8点~10点
10点~12点
8点~20点
解题思路:
优先排序
结束时间最早的会议优先,作为第一场
找剩下的结束时间最早的,而且开始时间要大于第一场结束,作为第二次
重复以上操作
class Me implements Comparable<Me> {
private int meNum; // 会议的编号
private int startTime; // 会议开始时间
private int endTime; // 会议结束时间
public Me(int meNum, int startTime, int endTime) {
this.setMeNum(meNum);
this.setStartTime(startTime);
this.setEndTime(endTime);
}
@Override
public int compareTo(Me o) { //以结束时间排序 早结束的放前面
if (this.endTime > o.endTime)
return 1;
return -1;
}
public int getMeNum() {
return meNum;
}
public void setMeNum(int meNum) {
this.meNum = meNum;
}
public int getStartTime() {
return startTime;
}
public void setStartTime(int startTime) {
this.startTime = startTime;
}
public int getEndTime() {
return endTime;
}
public void setEndTime(int endTime) {
this.endTime = endTime;
}
@Override
public String toString() {
return "Me [meNum=" + meNum + ", startTime=" + startTime + ", endTime=" + endTime + "]";
}
}
public class Meeting {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt(); //有N个会议
List<Me> mes = new ArrayList<>();
for(int i = 0 ; i < n; i ++) {
int start = cin.nextInt();
int end = cin.nextInt();
Me me = new Me(i+1, start, end);
mes.add(me);
}
mes.sort(null); //实现排序 这个排序其实就是 贪心的策略
int curTime = 0 ;
for(int i = 0;i < n; i ++) {
Me me = mes.get(i);
if(me.getStartTime() >= curTime) { //判断能不能开
System.out.println(me.toString());
//
curTime = me.getEndTime();
}
}
}
}
动态规划
核心思想:分解子问题,通过局部最大值得到全局最大。
背包问题:
小偷去某商店盗窃,背有一个背包,容量是50kg,现在有以下物品(物品不能切分),请问小偷应该怎么拿才能得到最大的价值?
重量 价值
物品1 10kg 60元
物品2 20kg 100元
物品3 40kg 120元
假设有5kg的袋子
物品只有一个,且不能拆分。
钱:6 10 12
Kg:1 2 4
我们把5kg袋子拆分成1kg这样的来计算,每个格子的意思就是当前袋子在这个容量下能装的最大价值,行表示每次加的物品
添加物品 | 1kg | 2kg | 3kg | 4kg | 5kg |
---|---|---|---|---|---|
加第一个物品 | 6 | 6 | 6 | 6 | 6 |
加第二个物品 | 6 | 10 | 16 | 10 | 16 |
加第三个物品 | 6 | 10 | 16 | 16 | 18 |
第二个进来时,如果此时袋子为2kg,我们知道在第一个物品进来时,2kg最多能装6块钱。这时候如果我们选择装第二物品那么袋子里面的钱为10块,然后剩余0kg,那么物品1就不能装了。然后发现10块大于袋子里面原来的6快,所以我们选择装第二个 丢掉第一个。
第三个物品进来,如果此时袋子为5kg,我们如果选择加第三个物品,那么袋子容积还剩1kg,能得12块,我们找到上一列1kg袋子的最大价值为6,所以总的为18。
public static void main(String[] args) {
int value[] = {60,100,120}; //每个物品的钱
int weight[] = {10,20,40}; //每个物品的重量 和上面的一一对应
int w = 50; //袋子的容积
int n = 3; //物品的个数
int dp[][] = new int[n+1][w+1]; //表示分割,成一个 小的表格
for(int i = 1; i<=n ;i++) { //表示物品往里面加
for(int cw = 1; cw <= w; cw ++) { //袋子在每一个容积下所装的最大的钱
if(weight[i - 1] <= cw) { //表示这个物品可以装
dp[i][cw] = Math.max(
value[i-1]+dp[i-1][cw-weight[i-1]], //我装新加的物品
dp[i-1][cw] //我不装这个新加的这个物品
);
}else {
dp[i][cw] = dp[i-1][cw]; //新加的这个装不下 ,那么就取前一个物品装值
}
}
}
System.out.println("袋子能装的最大价值:" + dp[n][w]);
}