通过减一或除以一个数将n变为1------贪心算法

初涉贪心算法,先将百度百科的定义贴下,留作慢慢体会:
贪心算法*(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解*。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
题目描述:
这里写图片描述
我原本的代码:

#include<stdio.h>
int main()
{
    long long a, b, n, k,x, cnt = 0;
    double t1, t2;
    scanf("%lld%lld%lld%lld", &n,&k,&a,&b);
    x = n;
    while (x != 1)
    {
        t1 = t2 = -1.0;
        t1 = 1.0/a;
        if (x%k == 0) t2 = 1.0*(x - x * 1.0 / k) /b ;
        if (t1 > t2) {
            x -= 1;
            cnt += a;
            continue;
        }
        x /= k;cnt += b;
    }
    printf("%lld", cnt);
    return 0;
}

分析:
最开始我的想法:按照性价比(也就是动态的用长度除以开销来计算)的思想,分两种情况:
(1)不能被k整除时,循环减一到整除,记录开销。
(2)可以被k整除时,计算减一和除以k的分别性价比,选优者记录开销。
这样做的结果是:测试数据没有太大问题,但是提交却总是Time Limit
分析下来原因有如下:
(1)未考虑特殊情况,即当 倍数k=1 的时候,只要是进行除法,n的值永远不变,这样不可能跳出循环。
(2)傻傻的用循环减一的方法来使n变得可以被k整除,进而讨论。这样反复循环,效率自然低得多。

改进的措施:
(1)考虑到 k==1 的情况,如果k==1,则此时只能用减一的方法来使n变为1
(2)对于循环减一直到整除的做法,改为一次性减到可以被整除
(3)可以被整除后,有两种策略 :a.直接除以k,得到 n/k ,减少的数目相当于 (n-n/k),开销为b; b.一直减一直到与(n-n/k)这个减少的数目相同为止,计算开销为 (n-n/k)*a通过比较前两种策略的优劣,可以加到cnt来得到局部最优解。
此时还存在一个问题,如果算到 n小于k时,循环开始时的减法会一直减到0(因为零除以任何非零数都得零),而我们期望得到的n为1,因此,这种情况需要另加判断,一旦算到这一步,就直接跳出循环,计算减到1所需的值加上即可。

改后代码:

#include<stdio.h>
int main()
{
    long long n, k, a, b;
    long long p, t1,t2,cnt = 0;
    scanf("%lld%lld%lld%lld", &n, &k, &a, &b);
    if (k == 1) {
        printf("%lld", (n - 1)*a);
        return 0;
    }
    while (n != 1)
    {
        if (n%k != 0) {     //直到可以整除再比较
            cnt += n%k *a;
            n-=n%k; //减到可以整除为止
        }   
        //如果减到可以整除时n仍大于1,就接着向下执行
        if (n > 1 && n%k == 0) {
            t1 = b; //直接除到的花销
            t2 = (n - n / k)*a; //一直减到刚才消去位置的花销
            if (t1 < t2) {
                cnt += t1;
            }
            else {
                cnt += t2;
            }
            n /= k;
        }
        if (n < k) break;
    }
    cnt += (n - 1)*a;
    printf("%lld", cnt);
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值