算法提升(6):01背包变种、有序表应用、int溢出边界讨论

题目1

牛牛准备参加学校组织的春游,出发前牛牛准备往背包里装入一些零食,牛牛的背包容量为w。牛牛家里一共有n袋零食,第i袋零食体积为v[i]。牛牛想知道在总体积不超过背包容量的情况下,他一共有多少种零食放法(总体积为0也算一种放法)。

思路

从左到右的尝试模型,拿或者不拿,当越界时拿的零食重量小于等于w就返回1。

//递归

int process(vector<int> v, int index, int rest);

int ways1(vector<int> v, int w)
{
    if (v.size() == 0 || w < 0)
    {
        return 0;
    }
    return process(v, 0, w);
}

int process(vector<int> v, int index, int rest)
{
    if (index == v.size())
    {
        return rest >= 0 ? 1 : 0;
    }
    return process(v, index + 1, rest - v[index]) + process(v, index + 1, rest);
}

//动态规划
int ways2(vector<int> v, int w)
{
    if (v.size() == 0 || w < 0)
    {
        return 0;
    }
    vector<vector<int>> dp(v.size() + 1, vector<int>(w + 1));
    for (int i = 0; i < dp[0].size(); i++)
    {
        dp[v.size()][i] = 1;
    }
    for (int i = v.size() - 1; i >= 0; i--)
    {
        for (int j = 0; j <= w; j++)
        {
            dp[i][j] = dp[i + 1][j - v[i]] + dp[i + 1][j];
        }
    }
    return dp[0][w];
}

题目2

为了找到自己满意的工作,牛牛收集了每种工作的难度和报酬。牛牛选工作的标准是在难度不超过自身能力值的情况下,牛牛选择报酬最高的工作。在牛牛选定了自己的工作后,牛牛的小伙伴们来找牛牛帮忙选工作,牛牛依然使用自己的标准来帮助小伙伴们。牛牛的小伙伴太多了,于是他只好把这个任务交给了你。

class Job
{
public:
    Job(int money, int hard)
    {
        this->money = money;
        this->hard = hard;
    }
public:
    int money;// 该工作的报酬
    int hard; //该工作的难度
};

给定一个Job类型的数组jobArr,表示所有的工作。给定一个int类型的数组arr,表示所有小伙伴的能力,每种工作不限人数,每人只能选一个工作。返回int类型的数组,表示每一个小伙伴按照牛牛的标准选工作后所能获得的报酬。

思路

先把jobArr按照hard从小到大排序,hard一样的就按照money从大到小排,排完序后hard一样的只保留money最多的,其他全部删掉,之后只剩下hard不同的,此时再删掉hard大但是money 比前面hard小的money 少 的job。然后遍历arr数组,选能力范围内hard最大的即可。

class Job
{
public:
    Job(int hard, int money)
    {
        this->money = money;
        this->hard = hard;
    }
public:
    int money;// 该工作的报酬
    int hard; //该工作的难度
};

class JobCompare   //job按照能力从小到大,能力相同的money从大到小排的规则排序
{
public:
    bool operator()(const Job &j1, const Job &j2)
    {
        if (j1.hard == j2.hard)
        {
            return j1.money > j2.money;
        }
        else
        {
            return j1.hard < j2.hard;
        }
    }
};

vector<int> mostMoney(vector<Job> jobArr, vector<int> arr)
{
    if (jobArr.size() < 1 || arr.size() < 1)
    {
        return {};
    }
    sort(jobArr.begin(), jobArr.end(), JobCompare());  //按照规则排序
    map<int, int> jobMap;
    jobMap.insert(make_pair(jobArr[0].hard, jobArr[0].money));
    Job preJob = jobArr[0];
    for (int i = 1; i < jobArr.size(); i++)   
    {
        if (preJob.hard != jobArr[i].hard && preJob.money < jobArr[i].money)     //hard一样的只保留money最多的,hard更大只保留money也更多的
        {
            jobMap.insert(make_pair(jobArr[i].hard, jobArr[i].money));
            preJob = jobArr[i];
        }
    }
    vector<int> ans(arr.size());
    for (int i = 0; i < arr.size(); i++)
    {
        auto it = jobMap.lower_bound(arr[i]);
        if (it == jobMap.begin() && it->first > arr[i])  //当前能力比所有工作的要求的能力都低
        {
            ans[i] = 0;
        }
        else if (it == jobMap.end())   //当前能力比所有工作要求的能力都高
        {
            ans[i] = jobMap.crbegin()->second;
        }
        else if (it->first == arr[i])   //当前能力找到了要求能力与其相同的工作
        {
            ans[i] = it->second;
        }
        else           //当前能力有工作可以做,找到了第一个要求能力大于它的工作,再往前一位就是能力要求比它小的工作
        {
            ans[i] = (--it)->second;
        }
    }
    return ans;
}

题目3

给定一个字符串,如果该字符串符合人们日常书写一个整数的形式,返回int类型的这个数;如果不符合或者越界返回-1或者报错。

思路

符合人们日常书写一个整数的形式有以下几条要求:

  1. 只有第一位允许是符号且为'-',如果第一位是'-',那么第二位不能是0
  2. 如果第一位是0,那么后面不能有数字

当满足上面的条件后,就可以转成整数,但是要求int类型,所以在转的过程中讨论溢出情况,具体看下面代码

bool isValid(string str);

int strToInt(string str)
{
    if (!isValid(str))
    {
        return -1;
    }
    int limit1 = INT_MIN / 10;
    int limit2 = INT_MIN % 10;     //两个防止溢出的限制
    bool isNeg = str[0] == '-' ? true : false;   //判断现在要转换的数字是正数还是负数。
    int ans = 0;
    for (int i = isNeg ? 1 : 0; i < str.size(); i++)   //负数下标为1的位置开始
    {
        int cur = '0' - str[i];     //不管现在转化的是正数还是负数,统一用负数转,因为INT_MIN的绝对值范围比INT_MAX的范围大1,如果要转换的数是INT_MIN,用正数转会溢出
        if (ans < limit1 || (ans == limit1 && cur < limit2))   //判断要转的数是否将要溢出。
        {                                                      //如果第一种情况中了,那么ans再乘以10就溢出,所以返回-1(注意ans是负数,所以是小于号,第二种情况同理)
            return -1;                                         //如果第二种情况中了,那么ans加上cur就溢出,所以返回-1
        }
        ans = ans * 10 + cur;
    }
    if (!isNeg && ans == INT_MIN)   //如果要转换的正数,但是转换结果是INT_MIN,则证明溢出
    {
        return -1;
    }
    return isNeg ? ans : -ans;
}

bool isValid(string str)   //讨论是否是合格的整数形式
{
    if (str.size() < 1)
    {
        return false;
    }
    if (str.size() == 1)
    {
        //只有一位,判断是否是0 ~ 9之间的数字
        return (str[0] >= '0' && str[0] <= '9');
    }
    if (str[0] == '-')
    {
        //第一位是符号'-'
        if (str[1] <= '0' || str[1] > '9')
        {
            //第一位是符号'-',所以要求第二位不能是0或者符号
            return false;
        }
        for (int i = 2; i < str.size(); i++)
        {
            //从第三位开始判断是否是0 ~ 9之间的数字
            if (str[i] < '0' || str[i] > '9')
            {
                return false;
            }
        }
    }
    else if(str[0] <= '0' || str[0] > '9')
    {
        //第一位是非'-'的符号或第一位是0
        return false;
    }
    else
    {
        //第一位是数字,从第二位开始判断是否是0 ~ 9之间的数字
        for (int i = 1; i < str.size(); i++)
        {
            if (str[i] < '0' && str[i] > '9')
            {
                return false;
            }
        }
    }
    return true;
}
posted @ 2022-08-12 20:48  小肉包i  阅读(18)  评论(0)    收藏  举报