概念:
所谓贪心算法是指,在对问题求解时总是做出在当前看来是最好的选择。在此种情况下做出的仅仅是在某种意义上的局部最优解。(但是局部最优解并不一定总是能得到全局最优解)
条件:
1.整体的最优解可以通过局部的最优解导出(贪心选择性质)
2.一个整体能够被分为多个子问题,并且这些子问题都能够求出最优解(最优子结构性质)
例题:
1.Saving HDU(杭电2111)
贪心思想:
每次取剩余中最有价值的宝物,直到没有容量为止。所以最先要用sort给结构体排序,使得宝物按价值降序。
代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
struct program
{
int a,c;
};
bool cmp(struct program d,struct program e){
return d.a>e.a;
}
int main(){
int n,i,sum,v;
while(scanf("%d",&v)!=EOF){
struct program b[101]={0};
if(v==0)
continue;
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d%d",&b[i].a,&b[i].c);
sort(b,b+i,cmp);//按宝物价值降序
sum=0;
for(i=0;i<n&&v>0;i++)
if(v>=b[i].c){
sum+=b[i].c*b[i].a;//计算所拿宝物总价值
v=v-b[i].c;//拿完之后剩余的容量
}
else{
sum+=v*b[i].a;
v=0;
}
printf("%d\n",sum);
}
return 0;
}
小结:
这题我做的时候贪心思想没问题,只是我把while(scanf(“%d”,&v)!=EOF)写成了while(1)结果一直不会结束,所以提交上去总是Time Limit Exceeded。主要是被“v为0的时候结束输入”误导了。
2.Crossing River
贪心思想:
这个问题主要集中在最快、次快、最慢、次慢的人身上让时间最短有两种方案,第一种让最快和次快过去,然后最快回来,再让最慢和次慢过去,然后次快回来;第二种就是让最快带最慢过去,再让最快带次慢过去,取两种方案中最短。sum=min(sum+a[1]*2+a[0]+a[i-1],sum+a[i-1]+a[0]+a[i-2]+a[0]);
当然也是要先用sort对结构体排序。
代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
int main(){
int T,N,i,sum;
scanf("%d",&T);
while(T--)
{
int a[1005]={0};
scanf("%d",&N);
for(i=0;i<N;i++)
{
scanf("%d",&a[i]);
}
sort(a,a+i);
sum=0;
while (i>3)
{
sum=min(sum+a[1]*2+a[0]+a[i-1],sum+a[i-1]+a[0]+a[i-2]+a[0]);//见贪心思想简析
i=i-2;
}
if(i==3)
{
sum+=a[0]+a[1]+a[2];
}
else if(i==2)
{
sum+=a[1];
}
else
{
sum+=a[0];
}
printf("%d\n",sum);
}
return 0;
}
小结:
虽然写出来很清晰但是想的时候真没想到,要两种方案进行比较还是同学告诉我的,希望以后思维能严谨一点吧。还有这题交了很多次都错了,只是因为我写了while(scanf(“%d”,&T)!=EOF),而题目是“The first line of the input contains a single integer T (1 <= T <= 20), the number of test cases. ”,改为scanf(“%d”,&T); while(T–)就对了。。。一定要注意这些细节。
3.Hero(杭电4310)
贪心思想:
这题我一直以为就是按照伤害降序然后按血量升序来攻击,结果怎么都不对,同学点出才知道原来是按照伤害和血量的比值来降序,这样才能使敌方对自己伤害最低,这也让我知道了提出贪心思想后验证很重要。
代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
struct enemyhero
{
double dps,hp;
};
bool cmp(struct enemyhero d,struct enemyhero e){
return d.dps/d.hp>e.dps/e.hp;//按伤害与血量的比值来降序
}
int main(){
int n,i;
int sumdps,sumhp;
while(scanf("%d",&n)!=EOF){
struct enemyhero b[21]={0};
sumdps=0;//总伤害
sumhp=0;//总减少血量
for(i=0;i<n;i++){
scanf("%lf%lf",&b[i].dps,&b[i].hp);
sumdps+=b[i].dps;
}
sort(b,b+i,cmp);
for(i=0;i<n;){
if(b[i].hp==0){
sumdps-=b[i].dps;//总伤害减去血量为0的敌人的伤害
i++;
}
b[i].hp-=1;
sumhp=sumhp+sumdps;
}
printf("%d\n",sumhp);
}
return 0;
}
小结:
这里我对那个结构体中的double很疑惑,因为结果的总丢失血量还是输出的整形,而为什么输入却要用浮点型。
struct enemyhero
{
double dps,hp;
};
后面才明白这是为了配合我bool函数里的除法。
bool cmp(struct enemyhero d,struct enemyhero e){
return d.dps/d.hp>e.dps/e.hp;
}
如果非要把结构体中dps和hp定义为整形,有两种方法:
bool cmp(struct enemyhero d,struct enemyhero e){
return d.dps*e.hp>e.dps*d.hp;
}
//或者
bool cmp(struct enemyhero d,struct enemyhero e){
double a,b;
a=1.0*d.dps/d.hp;
b=1.0*e.dps/e.hp;
return a>b;
}
完全想明白了这里我还是很开心的,虽然导致我今天题目没刷完,课后思考题也没想。。。
一般步骤:
1:开脑洞、凭直觉、或者根据生活经验等等得到原问题的规律
2:进行贪心猜想
3:证明第二步中的贪心想法是正确的(反证法,数学归纳法),或者是错误的(举反例)。(重要)
4:编写代码
个人感悟:
虽说是叫贪心算法,但我更想叫他贪心思想,我觉得提出一种贪心思想不难,主要就是验证难,思维稍微不太严谨就会是整个算法都是错的。然后我觉得一般这种求最少、最低、最好等等一般就是用贪心算法了