贪心算法及相关例题
一、基本概念
所谓贪心算法是指,在对问题求解时,总是做出“在当前看来是最好的选择”。也就是说,不从整体最优上加以考虑,它所做出的仅仅是在某种意义上的局部最优解。
不是对所有的问题都能得到整体最优解,选择贪心策略必须具备无后效性(即某个状态以后的过程不会影响以前的状态,只与当前状态有关)。
所以,对所采用的贪心策略一定要仔细分析其是否满足无后效性。
二、贪心算法的基本思路
- 建立数学模型来描述问题;
- 把求解问题分成若干个子问题;
- 对每个子问题求解,得到子问题的局部最优解;
- 把子问题的解局部最优解合成原来问题的一个解。
三、贪心算法存在的问题
- 不能保证求得的最后解就是最佳的;
- 不能用来求最大值或者最小值的问题;
- 只能求满足某些约束条件的可行解的范围。
四、贪心算法实用的问题
贪心策略适用的前提是:局部最优策略能导致产生全局最优解。
实际上,贪心算法使用的情况很少。一般对一个问题分析是否适用于贪心算法,可以先选择该问题下的几个实际数据进行分析,就可以做出判断。
五、贪心选择性质
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,换句话说,当考虑做何种选择的时候,我们只考虑对当前问题最佳的选择而不考虑子问题的结果。这是贪心算法可行的第一基本要素。贪心算法以迭代的方式作出相继贪心选择,每作一次贪心选择就讲所求问题简化为规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
六、贪心算法的实现框架
从问题的某一初始解出发:
while (朝给定总目标前进一步){
利用可行的决策,求出可行解的一个解元素。
}
由所有解元素组合成问题的一个可行解;
七、相关例题分析(更新中)
问题一:背包问题
①.0-1背包(不能用贪心算法!!!)
问题描述:给定n种物品和一个背包。物品i的重量是wi,其价值是vi,背包容量为c。应如何选择装入背包的物品,是的装入背包中物品的总价值最大?在选择装入背包物品时,对每种物品i只有2中选择(0或1,所以叫背包问题),即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分物品i。
②.背包
问题描述:
与0-1背包问题类似,所不同的是在选择物品i装入背包时,可以选择物品i的一部分,而不是一定要全部装入背包,i<i<n。
这2类问题都有最优子结构性质,极为相似,但背包问题可以用贪心算法求解,而0-1背包问题却不能用贪心算法求解。
问题分析:
首先计算每种物品单位重量的价值vi/wi,然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总重量未超过c,则选择单位重量价值次高的物品并尽可能多地装入背包。依次策略一直地进行下去,直到背包装满为止。
对于0-1背包问题,贪心算法之所以不能得到最优解是因为在这种情况下,它无法保证最终能将背包装满,部分闲置的背包空间使每公斤背包空间的价值降低了。事实上,在考虑0-1背包问题时,应比较选择该物品和不选择该物品所导致的最终方案,然后再作出最好的选择。由此就导出许多相互重叠的子问题。该类问题可采用动态规划算法。
void knapsack(int n,float m,float v[],float w[],float x[])
{
sort(n,v,w);
float c = m;
for(int i = 1;i <= n;i++)
x[i] = 0;
for(int i = 1;i <= n;i++)
{
if(w[i] > c)
break;
x[i] = 1;
c -= w[i];
}
if(i <= n)
x[i] = c/w[i];
}
问题二:花卉问题
小明现有N盆花排成一排,我们给出他们的坐标分别为A1,A2,…AN。现在小明最多能用M块宽度为1(长度不限)的木板去覆盖所有这些花,而木板都是直的,小明想知道他所需木板的最小长度。我们规定一个木板长度能盖住一个坐标单位。
数据范围:木板最大的数目M(1<=M<=100000);花的最大坐标(1<=S<=100000); 花的总数N(1 <= N <= S);花的坐标x(1 <= x <= S),计算盖住所有花盆的最小总长度。 输出所需木板的最小总长度作为答案。若两个花拥有一个坐标视为能用一个长度的木板覆盖。所有输入输入均为整数。
解答要求
时间限制:1000ms, 内存限制:64MB
输入
对于每组输入文件
第 1 行: 木板最大的数目M ,花的坐标范围S(表示在1…S内),花的总数N
第 2 到 N+1行: 每行包含一个整数,表示花的坐标。
输出
输出一个整数表示所需木板的最小总长度。
样例
输入样例 1
2 10 4
2
4
6
8
输出样例 1
6
代码样例(该代码暂时部分超时):
import java.util.Scanner;
public class Flower {
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
int M=s.nextInt();
int S=s.nextInt();
int N=s.nextInt();
int[] flowIndex=new int[N];
for (int i=0;i<N;i++){
flowIndex[i]=s.nextInt();
}
for (int i=N-1;i>=0;i--){
for (int j=0;j<i;j++){
if (flowIndex[j]>flowIndex[j+1]){
int num=flowIndex[j];
flowIndex[j]=flowIndex[j+1];
flowIndex[j+1]=num;
}
}
}
int distance[]=new int[flowIndex.length-1];
for (int i=0;i<flowIndex.length-1;i++){
if (flowIndex[i+1]-flowIndex[i]>=1){
distance[i]=flowIndex[i+1]-flowIndex[i]-1;
}else {distance[i]=0;}
}
for (int i=distance.length-1;i>=0;i--){
for (int j=0;j<i;j++){
if (distance[j]>distance[j+1]){
int num=distance[j];
distance[j]=distance[j+1];
distance[j+1]=num;
}
}
}
int length=flowIndex[flowIndex.length-1]-flowIndex[0]+1;
for (int i=0;i<M-1;i++){
length=length-distance[distance.length-1-i];
}
System.out.println(length);
}
}
后来对代码进行优化,将优化算法换成运行时间更短的桶排序(可以通过),代码如下:
import java.util.ArrayList;
import java.util.Scanner;
public class Flower {
public static void main(String[] args) {
Scanner s=new Scanner(System.in);
int M=s.nextInt();
int S=s.nextInt();
int N=s.nextInt();
int[] flowIndex=new int[N];
for (int i=0;i<N;i++){
flowIndex[i]=s.nextInt();
}
int[] flowindex=bucketSort(flowIndex);
int distance[]=new int[flowindex.length-1];
for (int i=0;i<flowindex.length-1;i++){
if (flowindex[i+1]-flowindex[i]>=1){
distance[i]=flowindex[i+1]-flowindex[i]-1;
}else {distance[i]=0;}
}
int[] dis=bucketSort(distance);
int length=flowindex[flowindex.length-1]-flowindex[0]+1;
for (int i=0;i<M-1;i++){
length=length-dis[dis.length-1-i];
}
System.out.println(length);
}
public static int[] bucketSort(int [] arr) {
int max = arr[0];
int min = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
int n = max - min + 1;
int[] b = new int[n];
for (int i = 0; i < arr.length; i++) {
b[arr[i] - min]++;
}
int[] output=new int[b.length];
ArrayList list=new ArrayList();
for (int i = 0; i < b.length; i++) {
while (b[i] != 0) {
output[i]=i+min;
list.add(i+min);
b[i]--;
}
}
int[] out=new int[list.size()];
for (int i=0;i<list.size();i++){
out[i]=(int)list.get(i);
}
return out;
}
}