训练第四天之贪心算法

概念:

所谓贪心算法是指,在对问题求解时总是做出在当前看来是最好的选择。在此种情况下做出的仅仅是在某种意义上的局部最优解。(但是局部最优解并不一定总是能得到全局最优解)

条件:

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:编写代码

个人感悟:

虽说是叫贪心算法,但我更想叫他贪心思想,我觉得提出一种贪心思想不难,主要就是验证难,思维稍微不太严谨就会是整个算法都是错的。然后我觉得一般这种求最少、最低、最好等等一般就是用贪心算法了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值