贪心(1)


  这一周讲的贪心算法部分,让我真心感受到这一算法的难度了,也终于感受到反复修改一个题的代码无法ac而抓耳挠腮的心情了。
  贪心算法,可以将一个复杂问题层层分解,得到的每一个小问题都能够选择出某一局部上的最优解,最终能够得出整个问题的结果。表面看起来,这一算法的思想与递归很类似,但真正实用时却大相径庭。首先,相似之处便是需要将问题层层分解成若干个小问题,通过解决这些小问题来拼凑出整个大问题的解。而,贪心算法之所以称之为“贪心”,就是因为他要进行某种选择,在每一个局部小问题中,我们会以某种贪心准则进行选择,选择出我们的局部最优解(从这里就可以看出,对问题的处理,最重要的就是你的if语句会怎样构造)。所以,解题的最终,重点就落在了所谓的贪心准则上了。
  贪心算法之所以更难,是因为贪心只是一种思想,而不像递归那样依托递归函数后形成一种大体固定的算法结构。真正做题时,只能本着贪心算法的思想,在千变万化的题目“见机行事”。所以说不同的人可能会得出不同的贪心策略,但并不一定所有的贪心策略都能够完美解决这道题(我深有体会)。鉴于现在刷题少,所以也不好意思做过多总结,先谈一下我在做题时遇到的问题和看法。
  第一,要读懂题目。
  在老师讲解的例题中,经常会碰到,看到这个题目的介绍却读不懂到底要我们做什么,常常需要老师解释一下才能明白题目的含义。
  
  问题描述
  有n个人排队到r个水龙头去打水,他们装满水桶的时间t1、t2…………tn为整数且各不相等,应如何安排他们的打水顺序才能使他们总共花费的时间最少?
  
  这个问题,在我初看之时认为,如果只有一个水龙头,这些人花费的总时间就是所有人打水花费的时间的总和。而看完别人对题目的分析后才明白,所谓花费的时间是指每个人单独打水和等待的时间,并求这些单独的时间的总和。如此看来,读题非常重要,一旦理解错意,解题就会偏离正轨,花费时间而得不到结果,特别是正式的比赛都是英文题目,读题更要小心。
  第二,尽量不要“暴力解题”。
  在这里插入图片描述
  这是oj贪心部分练习的第一个题,也是我做过的第一个英文题,鉴于迄今为止刷过的题实在少得可怜,就拿它开刀吧。
  题目的要求是将一个十进制数转化成二进制后(这个数只由0和1组成),求得另一个数,这个数与原数含有的“1”数目相同,但是所有含有相同“1”数目的数中大于原数的最小那一个。
  一般看到这个题后都会感觉,既然牵扯到二进制数中存在“1”的个数,所以应该将它表示出来,较好的办法是将它保存到字符数组中。但既然还需要再由这个数求出另一个数,将其表示为字符当然可以,但来回转化确实麻烦。
  因此,我想到了排序算法中的“桶排序”,而又有所不同。首先建立一个数组,用以存储这个数转化为二进制数后的“1”,而对于这些“1”,它们所能携带有意义的参数就是它们在二进制数中所在的位数以及个数。因此,我可以建立一个数组,将这个数二进制数中的“1”所在的位数由小到大取出放到数组中(下表为0的地址放弃,从下标为1的位置起开始存储),如此,得到的数组中,最大的下标值便是这些“1”的个数,而每一个数组元素存储的就是这个“1”在二进制数中所在的位置。
  接下来,接下来,只需要改变这些元素的值就可以改变这个数的大小而不影响其中“1”的个数。所以,贪心策略就是要选择最小可以改变的值就好了,同时,此位置前面所有的“1”位置必须全部排到对应二进制的最末位(只需要等于数组下标值就好)。既然是大于原数的最小符合要求的值,那么就可以依次以下标从小到大判断即可,判断的基准便是要改变的“1”需要移动到下一位(数组元素值加一),而它的下一位不能是‘1’。具体代码如下:

#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
int powcal(int);
void c(int);
int main()
{
    int n;
    for(;;)
    {
        cin >> n;
        if(n==0) break;
        else c(n);
    }
}
void c(int x)
{
    int num=0,j=0,k,a[100]={};
    for(int i=1,b=1;;i++)
    {
        if(x%2==1) {a[b]=i; b++; j++;}
        if(x<2) break;
        else x/=2;
    }
    for(int i=1;i<=j;i++)
    if(j==1) { a[j]++; break; }
    else if(i==j)
    {
        a[j]++;
        for(int s=j-1;s>0;s--)
        a[s]=s;
    }
    else if(a[i+1]!=a[i]+1)
    {
        a[i]++;
        for(int s=i-1;s>0;s--)
        a[s]=s;
        break;
    }
    for(int i=1;i<=j;i++)
    if(a[i]!=0) num+=powcal(a[i]-1);
    cout<< num<<endl;
}
int powcal(int x)
{
    int y=1;
    for(int i=1;i<=x;i++)
    y*=2;
    return y;
}

从这段代码中也能看出,其中有特殊情况不好归于同一段代码中,应该是我水平还不够,相信应该还可以更简便一点,但毕竟所用的思想还是一样的。或许这道题所用的贪心方法过简单,所以我甚至都不能确定我的贪心策略还称不称的上是贪心,但相信,解决一个问题的方法不是唯一的,贪心也可以同其他方法互化而用,也可以结合而用吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值