牛客上需要复习的华为机试题

1. 放苹果

https://www.nowcoder.com/practice/bfd8234bb5e84be0b493656e390bdebftpId=37&&tqId=21284&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

题目描述

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。

输入

每个用例包含二个整数M和N。0<=m<=10,1<=n<=10。

样例输入

7 3

样例输出

8

 

输入描述:

输入两个int整数

输出描述:

 

输出结果,int型

示例1

输入

7 3

输出

8

一开始的想法是对于每一个盘子,依次放0~m个苹果,然后考察剩下的苹果放到剩下的盘子里的情况,最后只剩1个盘子时返回1,即只有1种方法。

//错误,求的是排列数
int memo[11][11];
int calc(int m, int n)
{
    if(n == 1) return 1;
    if(memo[m][n] > -1) return memo[m][n];
    int ans = 0;
    for(int i = 0; i <= m; i++)
    {
        ans += calc(m - i, n - 1);
    }
    return memo[m][n] = ans;
}

但是这样得到的是不同放法的排列数,题目要的是不考虑顺序的放法总数。因此这种做法是错误的。

正确的做法是:

基础的情况是有m = 0或者n = 1,只有1种放法。

其它的情况分成两类:

(1) 有至少一个盘子是空的,那么放法等价于 f(m, n-1)。

(2) 所有盘子都至少有1个苹果(m >= n),那么放法等价于 f(m-n, n)。

所以, f(m, n) = f(m, n-1) + f(m-n, n)。

#include 
using namespace std;

int memo[11][11];
int calc(int m, int n)
{
    if(m == 0 || n == 1) return 1;
    if(memo[m][n] > -1) return memo[m][n];
    return memo[m][n] = calc(m, n-1) + (m >= n ? calc(m-n, n) : 0);
}

int main(){
    int m, n;
    memset(memo, -1, sizeof(memo));
    while(cin >> m >> n)
    {
        if(m < 0 || n <= 0) printf("-1\n");
        else printf("%d\n", calc(m, n));
    }
    return 0;
}

这种思路比较难想到,评论区有另外一种思路:使用规定顺序的深搜。

即按顺序在每一个盘子上分配,但是规定当前盘子的苹果数不超过上一个盘子里的苹果数,这样可以避免计算不同的排列。

#include 
using namespace std;

int dfs(int rest, int idx, int maxnum, int n)
{
    //苹果放完了
    if(rest == 0) return 1;
    //苹果没放完,盘子用完了
    if(idx == n) return 0;
    int ans = 0;
    //maxnum: 当前盘子最多能放的苹果数
    for(int i = maxnum; i >= 0; i--)
    {
        ans += dfs(rest - i, idx + 1, i, n);
    }
    return ans;
}

int main(){
    int m, n;
    while(cin >> m >> n)
    {
        if(m < 0 || n < 1) printf("-1\n");
        else printf("%d\n", dfs(m, 0, m, n));
    }
    return 0;
}

2. 按字节截取字符串

https://www.nowcoder.com/practice/a30bbc1a0aca4c27b86dd88868de4a4atpId=37&&tqId=21269&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

题目描述

编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。但是要保证汉字不被截半个,如"我ABC"4,应该截为"我AB",输入"我ABC汉DEF"6,应该输出为"我ABC"而不是"我ABC+汉的半个"。 

 

输入描述:

 

输入待截取的字符串及长度

输出描述:

 

截取后的字符串

示例1

输入

我ABC汉DEF
6

输出

我ABC

判题系统采用的是GBK编码,在GBK编码格式中,一个汉字占两个字节,并且这两个字节的首位都是1,也就是转化成整数值是负数。根据这个条件,可以确定最后一个字节是半个汉字还是ASCII字符。如果是ASCII字符,那么直接输出[0...n-1]所有的字节,即str.substr(0, n)。如果最后一个字节是半个汉字,那么还需要检查[0...n-2]范围内有多少个半个汉字,如果是偶数个,那么舍去最后一个字节, n--。最后输出str.substr(0,n)。

#include 
using namespace std;

int main(){
    string str;
    int num;
    while(cin >> str >> num)
    {
        if(str[num-1] < 0)
        {
            int cnt = 0;
            for(int i = 0; i < num - 1; i++)
            {
                if(str[i] < 0) cnt++;
            }
            if(!(cnt & 1)) num--;
        }
        printf("%s\n", str.substr(0, num).c_str());
    }
    return 0;
}

事实上,substr()函数会自动舍弃不完整的汉字,所以直接使用str.substr(0,n)也没问题。

3. 统计每个月兔子的总数

https://www.nowcoder.com/practice/1221ec77125d4370833fd3ad5ba72395tpId=37&&tqId=21260&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

有一只兔子,从出生后第3个月起每个月都生一只兔子,小兔子长到第三个月后每个月又生一只兔子,假如兔子都不死,问每个月的兔子总数为多少?

输入描述:

 

输入int型表示month

输出描述:

 

输出兔子总数int型

示例1

输入

9

输出

34

本质上是斐波那契问题,第n个月的兔子来自两个部分,第n-1个月已经有的兔子数,第n-2个月的兔子每个都会生出一只新兔子。所以 f(n) = f(n-1) + f(n-2)。

#include 
using namespace std;

int helper(int n)
{
    if(n <= 0) return 0;
    vector dp(n+1, 0);
    dp[1] = 1;
    for(int i = 2; i <= n; i++)
    {
        dp[i] = dp[i-1] + dp[i-2];
    }
    return dp[n];
}

int main(){
    int n;
    while(cin >> n)
    {
        printf("%d\n", helper(n));
    }
    return 0;
}

4.高精度整数加法

https://www.nowcoder.com/practice/49e772ab08994a96980f9618892e55b6tpId=37&&tqId=21280&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

字符串形式的 + - * 运算。

加法:

string add(string& a, string& b)
{
    if(a.size() < b.size()) a.swap(b);
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    while(b.size() < a.size()) b += '0';
    int carry = 0, sum = 0, n = a.size();
    string ans;
    for(int i = 0; i < n; i++)
    {
        sum = carry + a[i] - '0' + b[i] - '0';
        carry = sum / 10;
        sum %= 10;
        ans += '0' + sum;
    }
    if(carry) ans += '1';
    reverse(ans.begin(), ans.end());
    return ans;
}

减法:

string sub(string& a, string& b, bool& isNegtive)
{
    if(a.size() < b.size() || (a.size() == b.size() && a < b))
    {
        a.swap(b);
        isNegtive = true;
    }
    if(a == b)
    {
        return "0";
    }
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    while(b.size() < a.size()) b += '0';
    int n = a.size(), sub = 0, borrow = 0;
    string ans;
    for(int i = 0; i < n; i++)
    {
        sub = a[i] - b[i] - borrow;
        if(sub >= 0) ans += sub + '0';
        else
        {
            ans += sub + 10 + '0';
            borrow = 1;
        }
    }
    while(!ans.empty() && ans.back() == '0') ans.pop_back();
    reverse(ans.begin(), ans.end());
    return ans;
}

乘法 43. 字符串相乘:

class Solution {
private:
    //a和b已经是倒序,返回值也是倒序: 123 -> "321",
    string add(string& a, string& b)
    {
        if(a == "0") return b;
        else if(b == "0") return a;
        if(a.size() < b.size()) a.swap(b);
        while(b.size() < a.size()) b += '0';
        string ans;
        int n = a.size(), sum = 0, carry = 0;
        for(int i = 0; i < n; i++)
        {
            sum = a[i] - '0' + b[i] - '0' + carry;
            carry = sum / 10;
            sum %= 10;
            ans += sum + '0';
        }
        if(carry) ans += '1';
        return ans;
    }
    //a已经是倒序,返回值也是倒序
    string multi(string& a, int m)
    {
        if(m == 0) return "0";
        string ans;
        int product = 0, carry = 0, n = a.size();
        for(int i = 0; i < n; i++)
        {
            product = (a[i] - '0') * m + carry;
            carry = product / 10;
            product %= 10;
            ans += product + '0';
        }
        if(carry) ans += to_string(carry);
        return ans;
    }
public:
    string multiply(string num1, string num2) {
        if(num1 == "0" || num2 == "0") return "0";
        reverse(num1.begin(), num1.end());
        reverse(num2.begin(), num2.end());
        string ans;
        for(int i = 0; i < num2.size(); i++)
        {
            string tmp = multi(num1, num2[i] - '0');
            tmp = string(i, '0') + tmp;
            ans = add(ans, tmp);
        }
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

5. 矩阵乘法

https://www.nowcoder.com/practice/ebe941260f8c4210aa8c17e99cbc663btpId=37&&tqId=21292&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

矩阵乘法公式:对于 x * y 矩阵A 和 y*z矩阵B,它们的乘积C矩阵是 x * z矩阵,每个元素按下式计算:

C_i_j=\sum_{k=1}^yA_i_kB_k_j

#include 
using namespace std;

vector> helper(const vector>& mat1, const vector>& mat2, int x, int y, int z)
{
    vector> ans(x, vector(z, 0));
    for(int i = 0; i < x; i++)
    {
        for(int j = 0; j < z; j++)
        {
            for(int k = 0; k < y; k++)
            {
                ans[i][j] += mat1[i][k] * mat2[k][j];
            }
        }
    }
    return ans;
}

int main(){
    int x, y, z;
    while(cin >> x >> y >> z)
    {
        vector> mat1(x, vector(y));
        vector> mat2(y, vector(z));
        for(int i = 0; i < x; i++)
        {
            for(int j = 0; j < y; j++)
            {
                scanf("%d", &mat1[i][j]);
            }
        }
        for(int i = 0; i < y; i++)
        {
            for(int k = 0; k < z; k++)
            {
                scanf("%d", &mat2[i][k]);
            }
        }
        vector> ans = helper(mat1, mat2, x, y, z);
        for(const auto& row : ans)
        {
            for(int i = 0; i < row.size() - 1; i++)
            {
                printf("%d ", row[i]);
            }
            printf("%d\n", row.back());
        }
    }
    return 0;
}

6. 多线程

https://www.nowcoder.com/practice/cd99fbc6154d4074b4da0e74224a1582tpId=37&&tqId=21272&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

多线程编程

7.  24点游戏算法

4个数能否算出24点:每次取两个数,再放回数组, leetcode 原题 679. 24 点游戏

class Solution {
private:
    bool equal0(double a)
    {
        return abs(a) < 1e-9;
    }
    bool equal24(double a)
    {
        return abs(a - 24) < 1e-9;
    }
    bool helper(const vector& nums)
    {
        if(nums.size() == 2)
        {
            double a = nums[0], b = nums[1];
            return (equal24(a+b) || equal24(a-b) || equal24(b-a) || equal24(a*b) || (!equal0(b) && equal24(a/b)) || (!equal0(a) && equal24(b/a)));
        }
        for(int i = 0; i < nums.size(); i++)
        {
            for(int j = 0; j < nums.size(); j++)
            {
                if(i == j) continue;
                vector tmp;
                for(int k = 0; k < nums.size(); k++)
                {
                    if(k != i && k != j) tmp.push_back(nums[k]);
                }
                tmp.push_back(nums[i] + nums[j]);
                if(helper(tmp)) return true;
                tmp.pop_back();
                tmp.push_back(nums[i] - nums[j]);
                if(helper(tmp)) return true;
                tmp.pop_back();
                tmp.push_back(nums[i]*nums[j]);
                if(helper(tmp)) return true;
                tmp.pop_back();
                if(!equal0(nums[j]))
                {
                    tmp.push_back(nums[i] / nums[j]);
                    if(helper(tmp)) return true;
                    tmp.pop_back();
                }
            }
        }
        return false;
    }
public:
    bool judgePoint24(vector& nums) {
        vector t(nums.begin(), nums.end());
        return helper(t);
    }
};

附上输出所有可能的解的程序:

#include 
using namespace std;

double eps = 1e-9;
bool equals24(double x){return abs(x-24) < eps;}
bool equals0(double x) {return abs(x) < eps;}

struct Node
{
    double v;
    string s;
    Node(double v_  = 0): v(v_), s(to_string(static_cast(v_))){};
    Node(double v_ , string s_ ): v(v_), s(s_){};
};


unordered_set ans;
void helper(const vector& nums)
{
    if(nums.size() == 1)
    {
        if(equals24(nums[0].v)) ans.emplace(nums[0].s);
        return;
    }
    for(int i = 0; i < nums.size(); i++)
    {
        for(int j = 0; j < nums.size(); j++)
        {
            if(i == j) continue;
            vector next;
            for(int k = 0; k < nums.size(); k++)
            {
                if(k != i && k != j) next.emplace_back(nums[k]);
            }

            double v = nums[i].v + nums[j].v;
            string s = nums[i].s + "+" + nums[j].s;
            next.emplace_back(Node(v, s));
            helper(next);
            next.pop_back();

            v = nums[i].v - nums[j].v;
            s = "";
            s += nums[i].s + "-";
            if(nums[j].s.find('+') != string::npos)
                s += "(" + nums[j].s + ")";
            else s += nums[j].s;
            next.emplace_back(Node(v, s));
            helper(next);
            next.pop_back();

            v = nums[i].v * nums[j].v;
            s = "";
            if(nums[i].s.find('+') != string::npos || nums[i].s.find('-') != string::npos)
                s += "(" + nums[i].s + ")*";
            else s += nums[i].s + "*";
            if(nums[j].s.find('+') != string::npos || nums[j].s.find('-') != string::npos)
                s += "(" + nums[j].s + ")";
            else s += nums[j].s;
            next.emplace_back(Node(v, s));
            helper(next);
            next.pop_back();

            if(!equals0(nums[j].v))
            {
                v = nums[i].v / nums[j].v;
                s = "";
                if(nums[i].s.find('+') != string::npos || nums[i].s.find('-') != string::npos)
                    s += "(" + nums[i].s + ")/";
                else s += nums[i].s + "/";
                if(nums[j].s.find('+') != string::npos || nums[j].s.find('-') != string::npos)
                    s += "(" + nums[j].s + ")";
                else s += nums[j].s;
                next.emplace_back(Node(v, s));
                helper(next);
                next.pop_back();
            }
        }
    }
}

int main(void) {
    int a, b, c, d;
    scanf("%d%d%d%d", &a, &b, &c, &d);
    vector nums{Node(a), Node(b), Node(c), Node(d)};
    helper(nums);
    int idx = 1;
    for(const auto& s : ans) cout << idx++ << ": " << s << endl;
    return 0;
}

 

 

8.整数与IP地址间的转换

点分十进制字符串和十进制数之间相互转换。

https://www.nowcoder.com/practice/66ca0e28f90c42a196afd78cc9c496eatpId=37&&tqId=21256&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

值得借鉴的做法:位运算

#include
#include
#include
using namespace std;

int main()
{
    unsigned int a,b,c,d;
    char ch;
    while(cin>>a>>ch>>b>>ch>>c>>ch>>d)
    {
        cout<<((a<<24)|(b<<16)|(c<<8)|d)<>a;
        cout<<((a&0xff000000)>>24)<<"."<<((a & 0x00ff0000)>>16)<<"."<<((a&0x0000ff00)>>8)<<"."    <<(a & 0x000000ff)<

9. 合唱队

https://www.nowcoder.com/practice/6d9d69e3898f45169a441632b325c7b4tpId=37&&tqId=21247&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

计算最少出列多少位同学,使得剩下的同学排成合唱队形

说明:

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,   则他们的身高满足存在i(1<=i<=K)使得T1Ti+1>......>TK。

你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

最长递增子序列 + 最长递减子序列的问题:

如果以第 i 名同学做峰值,那么需要考察以他结尾的最长递增子序列的长度,作为左半部分;以他开始的最长递减子序列的长度,作为右半部分。最后计算选择哪一名同学做峰值能使留下的人最多即可。

 

#include 
using namespace std;

void helper(const int n, const vector& queue)
{
    vector lis(n, 1);
    vector lds(n, 1);
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < i; j++)
        {
            if(queue[i] > queue[j]) lis[i] = max(lis[i], lis[j] + 1);
        }
    }
    for(int i = n - 1; i >= 0; i--)
    {
        for(int j = i + 1; j < n; j++)
        {
            if(queue[i] > queue[j]) lds[i] = max(lds[i], lds[j] + 1);
        }
    }
    int maxsize = 0;
    for(int i = 0; i < n; i++) maxsize = max(maxsize, lis[i] + lds[i] - 1);
    printf("%d\n", n - maxsize);
}

int main(){
    int n;
    while(cin >> n)
    {
        vector queue(n);
        for(int i = 0;i < n; i++) scanf("%d", &queue[i]);
        helper(n, queue);
    }
    return 0;
}

10. 购物单

https://www.nowcoder.com/practice/f9c6f980eeec43ef85be20755ddbeaf4tpId=37&&tqId=21239&rp=1&ru=/ta/huawei&qru=/ta/huawei/question-ranking

有依赖关系的01背包问题。需要先把有依赖关系的所有可能组合列举出来,对于同一个被依赖对象,在这些组合中只能选1个最优的。

#include 
using namespace std;


int N, m;

void helper(const vector>>& all)
{
    m = all.size();
    vector> dp(m+1, vector(N+1, 0));
    //dp[i][j]: 从all[0...i-1]中选,和不超过j的最大价值
    for(int i = 1; i <= m; i++)
    {
        for(int j = 0; j <= N; j++)
        {
            dp[i][j] = dp[i-1][j];
            for(const auto& p : all[i-1])
            {
                if(j >= p.first) dp[i][j] = max(dp[i][j], dp[i-1][j-p.first] + p.second);
            }
        }
    }
    printf("%d\n", dp[m][N]);
}

int main()
{
    while(cin >> N >> m)
    {
        int vd, pd, qd;
        N /= 10;
        vector>> all;
        unordered_map v, p;
        unordered_map> q;
        unordered_set fujian;
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%d", &vd, &pd, &qd);
            v[i] = vd / 10;
            p[i] = vd * pd;
            if(qd > 0)
            {
                fujian.insert(i);
                q[--qd].push_back(i);
            }
        }
        for(int i = 0; i < m; i++)
        {
            if(fujian.count(i)) continue;
            if(!q.count(i))
            {
                vector> tmp;
                tmp.push_back({v[i], p[i]});
                all.emplace_back(tmp);
                continue;
            }
            if(q[i].size() == 1)
            {
                vector> tmp;
                tmp.push_back({v[i], p[i]});
                tmp.push_back({v[i] + v[q[i].front()], p[i] + p[q[i].front()]});
                all.emplace_back(tmp);
            }
            else
            {
                vector> tmp;
                tmp.push_back({v[i], p[i]});
                tmp.push_back({v[i] + v[q[i].front()], p[i] + p[q[i].front()]});
                tmp.push_back({v[i] + v[q[i].back()], p[i] + p[q[i].back()]});
                tmp.push_back({v[i] + v[q[i].front()] + v[q[i].back()], p[i] + p[q[i].front()] + p[q[i].back()]});
                all.emplace_back(tmp);
            }
        }
        helper(all);
    }
}

 

你可能感兴趣的:(牛客上需要复习的华为机试题)