贪心算法
所谓贪心算法,其实是一种思想,即通过把对问题的每个部分分别求部分最优解,从而解出整体最优解。
所以贪心算法特别适合最优解问题。
下面举两个例题。
(1)背包问题
给定一个载重量为M的背包,考虑n个物品,其中第i个物品的重量 ,价值wi (1≤i≤n),要求把物品装满背包,且使背包内的物品价值最大(物品可以分割)。
对于这个题目,就要引入性价比这一感念,即价值与质量的比值,性价比高的优先放入。
对于其实现
struct bag
{ //定义变量
int w; //物品的重量
int v; //物品的价值
double c; //性价比
double x; //装入背包的量,0≤x≤1
int index; //物品编号
}a[1001];
//存放物品的数组按照性价比排序
bool cmp(bag a, bag b)
{
return a.c >= b.c;
}
double knapsack(int n, bag a[], double c)
{
double cleft = c;//背包可携带质量
int i = 0;//计数
double b = 0;//价值计数
while(i<n && a[i].w<=cleft//物品质量小于背包容许容量)
{
cleft -= a[i].w;//求剩下还有多少容量
b += a[i].v;//价值计数
//物品原先的序号是a[i].index,全部装入背包
a[a[i].index].x = 1.0;
i++;
}
在while完成后可能有两种情况
(1)i>n 即物品全部放入背包
(2) a[i].w>cleft 背包剩余容量不足以在放进一整个物品
if (i<n)
{
a[a[i].index].x = 1.0*cleft/a[i].w;//将物品分割
b += a[a[i].index].x*a[i].v;//分割后价值
}
return b;
}
(2)最优装载问题
有一批集装箱要装上一艘载重量为c的轮船,其中集装箱i的重量为wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。
对这个问题来说,求的是可能多的集装箱装上轮船,是一个最优解问题。
于是,我们可以用贪心来做。因为不管体积,而且要尽量多的装,所以要选最轻的往上装。
struct load
{
int index; //集装箱编号
int w; //集装箱重量
}box[1001];
排序(按集装箱的重量升序):
bool cmp (load a, load b)
{
if (a.w<b.w) return true;
else return false;
}
while (scanf("%d%d", &c, &n)!=EOF)
{
memset(box, 0, sizeof(box));
memset(x, 0, sizeof(x));
for (int i=1; i<=n; i++)
{
scanf("%d", &box[i].w);
box[i].index = i;
}
//按集装箱的重量升序排序
stable_sort(box, box+n+1, cmp);
if (box[1].w>c) {
printf("No answer!\n");
continue;
}
//贪心算法的实现,重量最轻者先装载
int i;
for (i=1; i<=n && box[i].w<=c; i++)
{
x[box[i].index] = 1;
c -= box[i].w;
}
//输出装载的集装箱数量
printf("%d\n", i-1);
//输出装载的集装箱编号
for (i=1; i<=n; i++)
if (x[i]) printf("%d ", i);
printf("\n");
}
(3)删数问题
给定n位正整数a,去掉其中任意k≤n个数字后,剩下的数字按原次序排列组成一个新的正整数。对于给定的n位正整数a和正整数k,设计一个算法找出剩下数字组成的新数最小的删数方案。
从描述中可以看到,求的是剩下数字组成的新数最小的删数方案,是最优解问题。
对于这个题目,PPT上的代码是这样的
string a; //n位数a
int k;
cin>>a>>k;
//如果k≥n,数字被删完了
If (k >= a.size()) a.erase();
else while(k > 0)
{
//寻找最近下降点
int i;
for (i=0; (i<a.size()-1) && (a[i] <= a[i+1]); ++i);
a.erase(i, 1); //删除xi
k- -;
}
//删除前导数字0
while(a.size() > 1 && a[0] == '0')
a.erase(0, 1);
cout<<a<<endl;
对于这个代码,这里打错了
//寻找最近下降点
int i;
for (i=0; (i<a.size()-1) && (a[i] <= a[i+1]); ++i);
a.erase(i, 1); //删除xi
k- -;
}
这个会把小的那一个删除。
所以应该是这样的
//寻找最近下降点
int i;
for (i=0; (i<a.size()-1) && (a[i] <= a[i+1]); ++i);
a.erase(i+1, 1); //删除xi
k- -;
}
以上。