C++每日一练:详解-买铅笔&影分身&三而竭


前言

这回又换成C++了,Python要用C++也要用,没有哪个正经程序员只会一门语言的,咱可是CSDN认证带V的全栈攻城狮。今天的题目除了买铅笔都还是有点难度的,虽然影分身主要是考验阅读理解能力。
在这里插入图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、买铅笔

题目描述
P老师需要去商店买n支铅笔作为小朋友们参加编程比赛的礼物。她发现商店一共有 3 种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起 见,P老师决定只买同一种包装的铅笔。 商店不允许将铅笔的包装拆开,因此P老师可能需要购买超过 n 支铅笔才够给小朋 友们发礼物。 现在P老师想知道,在商店每种包装的数量都足够的情况下,要买够至少 n 支铅笔最少需要花费多少钱。

输入描述:
第一行包含一个正整数 n ,表示需要的铅笔数量。 接下来三行,每行用 2 个正整数描述一种包装的铅笔:其中第 1 个整数表示这种 包装内铅笔的数量,第 2 个整数表示这种包装的价格。 保证所有的 7 个数都是不超过 10000 的正整数。

输出描述:
1 个整数,表示P老师最少需要花费的钱。

示例:输入n=57, {{2, 2}, {50, 30}, {30, 27}} 输出:54

代码如下(示例):

using namespace std;
string solution(int n, int arr[3][2]){
    string result;
    // TODO:
    int minpay = 100000;
    for (size_t i=0; i<3; ++i){
        int tmp = arr[i][0];
        while (tmp<n){
            tmp += arr[i][0];
        }
        if ((tmp/arr[i][0])*arr[i][1] < minpay) minpay=(tmp/arr[i][0])*arr[i][1];
        result = to_string(minpay);
    }
    return result;
}

笔者做的题全是在原有代码的基础上改的,虽然为这点被坑了好多回,可就是不肯改。所以这里只给出solution函数就行了。

这题还是很简单的,代码中tmp是在不拆包装的情况下会买到的铅笔数,这里用的加法,其实也可以写成乘法的。乘法要判断是否整除的情况,可以用n/arr[i][0]+bool(n%arr[i][0]),运行速度应该差距不大。代码中的minpay用来表示最少地花费,比较一下取最小值就行。 需要注意的是minpay的初始值取大点,用题目所述的10000是不行的,好像只能得40分,费了我好几分钟才想到这值取小了…

二、影分身

题目描述
已知字符串str。字符串str包含字符’x’,’y’。 如果相邻的两个字符不同,消除两个字符,优先从左边进行消除。 xyyx - > yx ->

输入描述:
输入多个字符。(1<=len<=1e5)

输出描述:
输出最后的分身

示例:输入:xyyyy 输出:yyy

代码如下(示例):
这是利用了stack栈的后入先出(LIFO)的特性来做的写法,这个办法是能满分的。具体的思路就是将要保留的字符压入栈中,再和后一个对比,如示例xyyyy:
首先,定义了输入栈out,循环i=0时,因为栈是空的,就将x压入,continue继续下一个循环。i=1循环时,out的top是x、str[i] 是y,所以栈中的x被弹出,下一个循环栈又是空的了,i=2 的y被压入,以此类摔倒…直到循环完成就得到了解,因为栈的特性,只能从顶端取出数据,所以写了一个while循环,把栈中的字符又重新组合成字符串。栈的速度是极快的,比数组都要快,而且非常简单易用!用得上的函数方法就pop、push、size、top,有一点要注意的是,它和Python的list的pop方法不一样,j=out.pop(),是取不到值的。这个方法返回的是void, top()才返回栈顶的引用。

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <stack>

using namespace std;

std::string solution(std::string str){
    std::string result;
    // TODO:
    stack<char> out;
    int l = str.length();
    for (int i=0; i<l; ++i){
        if (out.size()==0){
            out.push(str[i]);
            continue;
        }
        if (str[i] == out.top()){
            out.push(str[i]);
        }else{
            out.pop();
        }
    }
    while (out.size()){
        char j = out.top();
        result = j + result;
        out.pop();
    }
    return result;
}

这题看了好久又多次测试才明白题意,它是每次消除以后要从新开始从左到右再计算的,不是第一次计算后保留下来的就一定会保留,还要再次和后面的字母比较,所以最终的结果只会有x或y。理解这一点后,就相对比较简单了,一开始笔者写了个递归来做,测试用例是没问题的。但实际执行会提示:terminate called after throwing an instance of 'std::bad_alloc'内存分配不足了!果然递归不靠谱。这里也给出递归写法以供参考:

string reduce(string str){
    int l = str.length();
    if (l==0 || l==1){
        return str;
    } 
    for (int i=0; i<l; ++i){
        if(str[i]==str[i+1] && i+1==l-1){
            return str;
        } 
         else if(str[i]!=str[i+1] && i+1<l){
            str=str.substr(0,i)+str.substr(i+2,l);
            break;
        } 
    }
    return reduce(str);
}

这个写法相对前面的代码显得很简洁,思路也简单,退出递归的条件是三个:一是消完了,二是只剩下一个字符,三是全部字符都一样。后面就是每次递归消除二个字符。测试发现不能过关,才改成stack栈的写法,说明测试用例中有极长的字符串。

三、三而竭

这题之前做过了,难度系数在笔者看来还是有点高的,当时也费了不少时间才解出来,主要是容易超时。是一个凑数字的题,暴力凑的方法,其中几个数的取值范围很重要。笔者也没有想出其它更好的办法,按这个解法用了600多ms的时间,算是免费过关。请见:

https://blog.csdn.net/alal001/article/details/130119027


总结

希望本文能对各位看官有所启发,原创文章,未经许可,请勿转载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无证的攻城狮

如本文对您有用,大爷给打个赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值