2018年校招全国统一模拟笔试(五月场)编程题集合 - 题解

题目链接:点这儿。

做了这套题,感受就是数据有点弱(第三题和第四题的数据弱,错误的算法也能过,建议牛客网加强数据)。题倒是很常规,考察了最短路算法,背包问题,贪心,字符串模拟和找规律。

第一题:牛牛取快递

题目

牛牛的快递到了,他迫不及待地想去取快递,但是天气太热了,以至于牛牛不想在烈日下多走一步。他找来了你,请你帮他规划一下,他最少要走多少距离才能取回快递。

输入描述

每个输入包含一个测试用例。
输入的第一行包括四个正整数,表示位置个数N (2<=N<=10000) ( 2 <= N <= 10000 ) ,道路条数M (1<=M<=100000) ( 1 <= M <= 100000 ) ,起点位置编号S (1<=S<=N) ( 1 <= S <= N ) 和快递位置编号T (1<=T<=N) ( 1 <= T <= N ) 。位置编号从1N道路是单向的。数据保证S≠T,保证至少存在一个方案可以从起点位置出发到达快递位置再返回起点位置。
接下来M行,每行包含三个正整数,表示当前道路的起始位置的编号U (1<=U<=N) ( 1 <= U <= N ) ,当前道路通往的位置的编号V (1<=V<=N) ( 1 <= V <= N ) 和当前道路的距离D (1<=D<=1000) ( 1 <= D <= 1000 )

输出描述

对于每个用例,在单独的一行中输出从起点出发抵达快递位置再返回起点的最短距离。

样例

in:
3 3 1 3 
1 2 3 
2 3 3 
3 1 1

out:
7

解析

单源最短路径,从起点到终点做一遍,从终点到起点做一遍,用vector mp[n + 1]建领接表的时候要注意边是单向边;

spfa s p f a 算法或堆优化的迪杰斯特拉算法(可以参见堆优化的迪杰斯特拉算法 - 社交网络图中结点的“重要性”计算这篇博文)都可以。这里用的是 spfa s p f a 算法。

代码

#include 

using namespace std;

const int INF = 0x3f3f3f3f;

int spfa(int src, int des, vector< pair<int, int > > mp[], int n)
{
    vector<int> dis(n + 1, INF);
    vector<bool> used(n + 1, false);
    queue<int> que({src});
    dis[src] = 0;
    used[src] = true;
    while (!que.empty()) {
        auto u = que.front();
        que.pop();
        used[u] = false;
        for (auto it = mp[u].begin(); it != mp[u].end(); ++it) {
            auto v = it->first, w = it->second;
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                if (!used[v]) {
                    used[v] = true;
                    que.push(v);
                }
            }
        }
    }
    return dis[des];
}

int main()
{
    for (int n, m, src, des; cin >> n >> m >> src >> des; ) {
        assert(n > 0);
        vector< pair<int, int> > mp[n + 1];
        for (int i = 0, u, v, w; i < m; i++)
            cin >> u >> v >> w, mp[u].emplace_back(v, w);
        cout << spfa(src, des, mp, n) + spfa(des, src, mp, n) << endl;
    }
    return 0;
}

第二题:牛牛炒股票

题目

牛牛得知了一些股票今天买入的价格和明天卖出的价格,他找犇犇老师借了一笔钱,现在他想知道他最多能赚多少钱。

输入描述

每个输入包含一个测试用例。
输入的第一行包括两个正整数,表示股票的种数N (1<=N<=1000) ( 1 <= N <= 1000 ) 和牛牛借的钱数M (1<=M<=1000) ( 1 <= M <= 1000 )
接下来N行,每行包含两个正整数,表示这只股票每一股的买入价X (1<=X<=1000) ( 1 <= X <= 1000 ) 和卖出价Y (1<=Y<=2000) ( 1 <= Y <= 2000 )
每只股票可以买入多股,但必须是整数。

输出描述

输出一个整数表示牛牛最多能赚的钱数。

样例

in:
3 5 
3 6 
2 3 
1 1

out:
4

解析

赔钱或者不赚钱的股票不要买,剩下的就是赚钱的股票,一只股票买进要x元,卖出赚y元,问用m元可以最多赚多少钱,这是一个裸的完全背包问题

但是做完题后看了下其他同学的代码,居然有人用贪心水过去了,数据还是要加强啊。

代码

#include 

using namespace std;

int main()
{
    for (int n, m; cin >> n >> m; ) {
        vector< pair<int, int> > arr;
        for (int i = 0, x, y; i < n; i++) {
            cin >> x >> y;
            if (y > x)
                arr.emplace_back(x, y - x);
        }
        vector<int> dp(m + 1, 0);
        for (auto it = arr.begin(); it != arr.end(); ++it)
            for (int i = 0; i * it->first <= m; i++)
                for (int j = i * it->first; j <= m; j++)
                    dp[j] = max(dp[j], dp[j - i * it->first] + i * it->second);
        cout << dp[m] << endl;
    }
    return 0;
}

第三题:独立的牛牛

题目

小牛牛为了向他的父母表现他已经长大独立了,他决定搬出去自己居住一段时间。
一个人生活增加了许多花费: 牛牛每天必须吃一个水果并且需要每天支付x元的房屋租金。
当前牛牛手中已经有f个水果和d元钱,牛牛也能去商店购买一些水果,商店每个水果售卖p元。
牛牛为了表现他独立生活的能力,希望能独立生活的时间越长越好,牛牛希望你来帮他计算一下他最多能独立生活多少天。

输入描述

输入包括一行,四个整数x, f, d, p (1<=x,f,d,p<=2109) ( 1 <= x , f , d , p <= 2 ∗ 10 9 ) ,以空格分割

输出描述

输出一个整数, 表示牛牛最多能独立生活多少天。

样例

in:
3 5 100 10

out:
11

解析

先不考虑买苹果,看看牛牛持有的苹果和钱能不能撑过f天,如果不能,就按钱来算天数;

如果牛牛还有闲钱剩,那么每一天就需要p元买苹果和x元交房租,那么拿剩下的钱除一下x + p就行了。

代码

#include 

using namespace std;

int main()
{
    using LL = long long;
    for (LL x, f, d, p; cin >> x >> f >> d >> p; ) {
        LL res = d - x * f;
        if (res < 0) {
            cout << d / x << endl;
        } else {
            cout << f + res / (x + p) << endl;
        }
    }
    return 0;
}

第四题:牛牛吃雪糕

题目

最近天气太热了,牛牛每天都要吃雪糕。雪糕有一盒一份、一盒两份、一盒三份这三种包装,牛牛一天可以吃多盒雪糕,但是只能吃六份,吃多了就会肚子疼,吃少了就会中暑。而且贪吃的牛牛一旦打开一盒雪糕,就一定会把它吃完。请问牛牛能健康地度过这段高温期么?

输入描述

每个输入包含多个测试用例。
输入的第一行包括一个正整数,表示数据组数T (1<=T<=100) ( 1 <= T <= 100 )
接下来N行,每行包含四个正整数,表示高温期持续的天数N (1<=N<=10000) ( 1 <= N <= 10000 ) ,一盒一份包装的雪糕数量A (1<=A<=100000) ( 1 <= A <= 100000 ) ,一盒两份包装的雪糕数量B (1<=B<=100000) ( 1 <= B <= 100000 ) ,一盒三份包装的雪糕数量C (1<=A<=100000) ( 1 <= A <= 100000 )

输出描述

对于每个用例,在单独的一行中输出结果。如果牛牛可以健康地度过高温期则输出"Yes",否则输出"No"

样例

in:
4 
1 1 1 1 
2 0 0 4 
3 0 2 5 
4 24 0 0

out:
Yes 
Yes 
No 
Yes

解析

我一开始用下面这种贪心做的。

贪心,用1 * a, 2 * b , 3 * c去凑6 * n,先用c去凑,然后再用b凑,最后用a凑。

于是写出了下面的代码:

#include 

using namespace std;

int main()
{
    int T;
    cin >> T;
    for (int n, a, b, c; T--; ) {
        cin >> n >> a >> b >> c;
        int sum = 6 * n;
        sum = sum > 3 * c ? sum - c * 3 : sum % 3;
        sum = sum > 2 * b ? sum - b * 2 : sum % 2;
        cout << (sum <= a ? "Yes" : "No") << endl;
    }
    return 0;
}

过了,但是后来一想不对,数据弱了导致错误的贪心算法也过了。举一个例子1 0 3 1(建议牛客网加上这个数据),没有一份一盒的,有3个两份一盒的,有1个三份一盒的,按照上面的贪心做法,先吃掉三份一盒的,牛牛还需要吃三份才能度过危机,这时却发现剩下的3个两份一盒的却怎么也凑不出3份来!如果直接吃3盒一盒两份的雪糕,牛牛是可以度过危机的,但是上面这个代码却说不能。

正确的吃法确实要先吃三份一盒(一天吃两盒),但是,如果发现吃完三盒一份的还剩下一盒,这时候这一盒三份的就要和一盒两份的和一盒一份的凑着吃或者这一盒三份的和3个一盒一份的凑着吃(我上面的那种贪心算法就是把这一盒三份的和一盒两份的凑,没有用一盒一份的凑),这是因为如果你独吃这个三份一盒的,你就会留下一个奇数,这个奇数有可能用一份一盒的和两份一盒的凑不齐(就是上面的那个例子);然后再吃两份一盒的(一天吃三盒),这时如果剩下一盒两份的,要不要先拿一盒一份的来凑呢,不需要的,因为剩下的数一定是偶数,而用若干个1和若干个2一定是可以凑出一个偶数的;然后再吃一份一盒的(一天吃六盒);最后看下剩下的两盒一份的和剩下的一盒一份的能不能再凑出一份口粮来。

另外,也看了其他同学通过的代码,发现有一位同学的没有判断一盒三份的和3个一盒一份的凑的代码也过了,建议牛客网加上这组数据1 3 0 1

代码

#include 

using namespace std;

int main()
{
    int T;
    cin >> T;
    for (int n, a, b, c, ans; T--; ) {
        cin >> n >> a >> b >> c;
        ans = c / 2, c %= 2;
        if (c == 1) {
            if (b && a)
                --b, --a, ++ans;
            else if (!b && a >= 3)
                a -= 3, ++ans;
        }
        ans += b / 3, b %= 3;
        ans += a / 6, a %= 6;
        ans += 2 * b + a >= 6;
        cout << (ans >= n ? "Yes" : "No") << endl;
    }
    return 0;
}

第五题:牛牛打响指

题目

牛牛在地上捡到了一个手套,他带上手套发现眼前出现了很多个小人,当他打一下响指,这些小人的数量就会发生以下变化:如果小人原本的数量是偶数那么数量就会变成一半,如果小人原本的数量是奇数那么数量就会加一。现在牛牛想考考你,他要打多少次响指,才能让小人的数量变成1。

输入描述

每个输入包含一个测试用例。
输入的第一行包括一个正整数,表示一开始小人的数量N (1<=N<=10100) ( 1 <= N <= 10 100 )

输出描述

对于每个用例,在单独的一行中输出牛牛需要打多少次响指才能让小人的数量变成1。

样例

in:
10000

out:
20

解析

我的做法是把这个数字化成二进制,这个数字很大达到了 10100 10 100 ,这时用字符串模拟短除法就行了。

10000 10000 的二进制是 (10011100010000)2 ( 10011100010000 ) 2 ,先不要管除2这个动作(也就是说末尾的0先不看,最后移位一下子就移掉了),先来看从右到左的第一个1,执行加1动作变成了 (10011100100000)2 ( 10011100100000 ) 2 ,再执行一次加1动作,变成了 (10011101000000)2 ( 10011101000000 ) 2 ,依次类推,这个二进制依次是 (10011110000000)2 ( 10011110000000 ) 2 (10100000000000)2 ( 10100000000000 ) 2 (11000000000000)2 ( 11000000000000 ) 2 (100000000000000)2 ( 100000000000000 ) 2

模拟完你就能发现规律了,如果末尾都是零,那么答案就是零的个数;如果除了首位的1,后面还有1,就从最右边的1开始往左边数0的个数,假如有x个0,那么就会执行x + 1次加一操作,这个时候序列才会变成末尾全是0,而且,这个时候还进位了,这个时候就把答案再加上这个时候的二进制末尾零的个数。

代码

#include 

using namespace std;

int main()
{
    for (string str; cin >> str; ) {
        string bin;
        for (string tmp; ; tmp.clear()) {
            int sum = 0, ok = true;
            for (auto it = str.begin(); it != str.end(); ++it) {
                if (*it != '0')
                    ok = false;
                sum = sum * 10 + *it - '0';
                tmp.push_back(sum / 2 + '0');
                sum %= 2;
            }
            if (ok)
                break;
            bin.push_back(sum + '0');
            str.swap(tmp);
        }
        reverse(bin.begin(), bin.end());
        int ans = bin.size() - 1;
        auto it = bin.rbegin();
        for (; it != bin.rend() && *it == '0'; ++it) {}
        if (next(it) != bin.rend())
            ans += 1 + 1 + count(it, bin.rend(), '0');
        cout << ans << endl;
    }
    return 0;
}

你可能感兴趣的:(笔试面试题,笔试面试题)