哈理工新生赛题解

A小亮的睡眠时间

思路:求一下一共花了多少时间思考,注意思考时间大于睡觉时间上限的特殊情况。

复制代码
#include <iostream>

using namespace  std;

int main()

{
    int n;
    scanf("%d", &n);
    int sum = 0;
    int cur;
    int total = 450;
    int h = 0;
    int s = 0;
    for(int i = 0; i < n; i++)

    {

        scanf("%d", &cur);

        sum += cur;

    }
    sum = sum * 20;
    total = total - sum;
    if(total < 0)

    {
        total = 0;
    }
    h = total / 60;
    s = total % 60;
    printf("%02d:%02d\n", h, s);
}

B 小亮是个大忙人

思路:考察阅读理解+前缀和差分

  1. 题意写的很清楚,对于路程A到B(A<B),小亮要经过区间[A,B]中的所有站点,因此需要经过连接这些站点的路径,所以我们考虑用差分+前缀和统计每段路程的通过次数。

  2. 还需要明确一点,那就是从A到B和从B到A,这两种情况是等价的,都会走过相同的路径,所以在计数的时候可以同质化处理,为了维护差分的左小右大性质,在差分统计路段次数的时候还需要让min(A,B)在左,max(A,B)在右。

  3. 统计完路径次数以后,统计答案的时候,考虑对于通过k次的路段,是买k次不优惠的票还是买一个优惠卡然后买k次优惠的票合算即可。

  4. 最大优惠其实就是统计总花费的时候,在所有最合算的路段花费中选择一个最大值输出。

复制代码
#include <iostream>

using namespace std;

#define int long long

const int N = 1e5 + 5;

int p[N], a[N], b[N], c[N], s[N], n, S, m, l, r, res, maxv;

signed main()

{

    ios::sync_with_stdio(false), cin.tie(0);

    cin >> m >> n >> S;

    for (int i = 1; i <= n; i++)

        cin >> p[i];

    for (int i = 2; i <= m; i++)

        cin >> a[i] >> b[i] >> c[i];

    for (int i = 1; i < n; i++)

    {

        l = p[i], r = p[i + 1];

        if (l > r)

            swap(l, r);

        s[l + 1]++, s[r + 1]--;

    }

    for (int i = 2; i <= m; i++)

        s[i] += s[i - 1];

    for (int i = 2; i <= m; i++)

        res += min(s[i] * a[i], s[i] * b[i] + c[i]);

    for (int i = 2; i <= m; i++)

        maxv = max(maxv, min(s[i] * a[i], s[i] * b[i] + c[i]));

    if (res <= S)

        cout << res - maxv << endl

             << maxv << endl;

    else

        cout << "Infinity" << endl;

}

C小亮圆皮:
思路:看似数据结构实则贪心

  1. 对于长度为2的区间,一定有一个最大值一个最小值,此时我们获得有效值(max-min)。

  2. 对于长度更大,包含长度为2的区间的区间,由于这个区间包含刚才的区间,因此区间内的max不会减少,而区间内的min不会增加,二者差值不会变得更小。

  3. 因此,只需在所有区间长度为2的区间里取一个最小差值即可。

复制代码
#include <iostream>

using namespace std;

const int N = 1e5 + 5;

int n, a[N], res = 0x3f3f3f3f;

int main()

{

    ios::sync_with_stdio(false), cin.tie(0);

    cin >> n;

    for (int i = 1; i <= n; i++)

        cin >> a[i];

    for (int i = 1; i < n; i++)

        res = min(res, abs(a[i] - a[i + 1]));

    cout << res << endl;

}

D小亮想零元购:
思路:一道分类讨论的贪心

  1. 我们把所需凑出的价格分为需要多少k元货币rk和需要多少1元货币r1。

  2. 如果我们手里的k元货币ak足够,则答案为max(0,r1-a1),剩下的一元货币有多少用多少,多退少补即可。

  3. 如果我们手里的k元货币ak不够凑出rk个,那也先把ak个k元货币用了,得到rk=rk-ak为还需凑出的k元货币个数。

(1)接下来讨论1元货币,如果已有的1元货币多余所需1元货币,则有a1>r1,我们先把这些1元货币用了,然后剩下多少就去凑k元货币。由于k元货币的价格和1元货币都一样,都算一张货币,因此如果我们的1元货币不够凑出完整的k元货币,那还不如直接买一张k元货币,因此这种情况下的答案是max(0, rk - a1 / k)。

(2)如果手里的1元货币不足以支撑所需,那么差多少张1元买多少张一元,差多少张k元,买多少张k元,答案为 (r1 - a1) + rk。

复制代码
#include <iostream>

using namespace std;

int t, m, k, r1, rk, a1, ak;

int main()

{

    ios::sync_with_stdio(false), cin.tie(0);

    cin >> t;

    while (t--)

    {

        cin >> m >> k >> a1 >> ak, r1 = m % k, rk = m / k;

        if (ak > rk)

            cout << max(0, r1 - a1) << "\n";

        else

        {

            rk -= ak;

            if (a1 > r1)

                a1 -= r1, cout << max(0, rk - a1 / k) << "\n";

            else

                cout << (r1 - a1) + rk << "\n";

        }

    }

}

E. 小亮带发明家

思路:性质题,跟我讲的硬币翻转很像。

  1. 首先考虑一个显然的性质,那就是翻转硬币不可能超过一次。

  2. 题目要求我们每次翻转都是从头开始翻任意个,那么考虑一个O(N)的策略:用flag记录当前已经确定的前k个煎饼的状态(初始为第一个煎饼的状态),然后查看第k+1个煎饼的状态,如果跟flag一致,则前k+1个煎饼的状态仍为flag;如果第k+1个煎饼的状态跟flag不同,则把前k个煎饼全翻面并更改flag,我们仍然获得了前k+1个煎饼的状态。

  3. 如此,可O(N)计算答案,每次更改flag对应了一次翻面,答案为flag更改的次数。注意,最后题目要求所有煎饼正面朝上,因此如果最终前n个煎饼的flag为0,还需全部翻面,次数加1。

复制代码
#include <iostream>

#include <string>

using namespace std;

string s;

char flag;

int res;

int main()

{

    cin >> s, flag = s[0];

    for (int i = 1; i < s.size(); i++)

        if (flag != s[i])

            flag = s[i], res++;

    if (flag != '1')

        res++;

    cout << res << endl;
}

F.小亮xor小亮

思路:首先你要知道异或有个性质,那就是自己异或自己得0。

  1. 根据题意,可以得知:b异或一遍等价于把(a异或一遍)xor(x异或n次),我们想让这个数为0。

  2. 我们令a数组异或的最终值为ans。

  3. 所以当n为偶数的时候,x总是两两相消,所以当ans本身就为0的时候,x是谁都可以(值域内任何一个数)题目要求输出最小的,那就输出0,当ans本身不为0的时候,x是谁都不行(输出-1)。

  4. 当n位奇数的时候,x会余下来一个,则x xor ans = 0,那么x就等于ans。

复制代码
#include <bits/stdc++.h>

using namespace std;

int main()

{

    ios;

    int t;

    cin >> t;

    while (t--)

    {

        int n;

        cin >> n;

        int ans;

        cin >> ans;

        for (int i = 1; i <= n - 1; i++)

        {

            int x;

            cin >> x;

            ans ^= x;

        }

        if (n & 1)

        {

            cout << ans << endl;

        }

        else

        {

            if (ans == 0)

                cout << 0 << endl;

            else

                cout << -1 << endl;

        }

    }

 

    return 0;

}

G.彬彬有礼的小亮

思路:类似新生赛的那道"说的道理"。

  1. 从头开始扫一遍,每个位置的找一下子串是否符合目标子串,不符合就往下走看下一个位置,如果符合,就让下标直接跳过子串长度,去看后面的位置,
复制代码
#include <iostream>

#include <string>

using namespace std;

int main(){

    string s,t;

    cin >> s >> t;

    int ans = 0;

    for(int i = 0;i < s.size();i ++){

        if(t[0] == s[i]){

            bool flag = 1;

            for(int j = 0;j < t.size();j ++){

                if(t[j] != s[i+j]) {

                    flag = 0;

                    break;

                }

            }

            if(flag){

                ans ++;

                i += t.size()-1;

            }

        }

    }

    

    cout << ans << endl;

}

H. 小亮学图像处理

思路:直接预处理出来一个足够长的字母串,然后根据题意模拟即可,注意细节,多调几次就过了,看代码即可。

复制代码
#include <iostream>

#include <string>

using namespace std;

int main(){

    string tmp = "abcdefghijklmnopqrstuvwxyz";

 

    string str = "";

    for(int i = 1;i <= 10;i ++){

        str += tmp;

    }

    int n;

    cin >> n;

    for(int i = 0;i < n/2+1 ;i ++){

        for(int j = 0;j < n/2+1  ;j ++){

            cout<<str[j + i];

        }

        for(int j = n/2-1;j >= 0;j --){

            cout<<str[j + i];

        }

        cout<<endl;

    }

    for(int i = n/2-1;i >=0;i --){

        for(int j = 0;j < n/2+1  ;j ++){

            cout<<str[j + i];

        }

        for(int j = n/2-1;j >= 0;j --){

            cout<<str[j + i];

        }

        cout<<endl;

    }

}

I. 骰子大师小亮:
思路:各个骰子之间是独立的,只需模拟即可,开一个数组记录输入的序列,然后开另一个数组初始化全为7,然后开始模拟扔骰子,每次让这些数组往下减一,如果减完以后的数和输入的序列一致,那就再减一。

复制代码
#include <iostream>

using namespace std;

int b[7] = {7, 7, 7, 7, 7, 7, 7}, a[7], n;

int main()

{

    for (int i = 1; i <= 6; i++)

        cin >> a[i];

    cin >> n;

    while (n--)

    {

        for (int i = 1; i <= 6; i++)

        {

            b[i]--;

            if (b[i] == a[i])

                b[i]--;

        }

    }

    for (int i = 1; i <= 6; i++)

        cout << b[i] << " ";

    cout << endl;

}

J. 小亮被网络诈骗

思路:用map记录字母出现个数,然后看看map存了多少个字母(size)即可。

复制代码
#include <bits/stdc++.h>

using namespace std;

int main()
{

    map<char, int> m;

    string str;

    cin >> str;

    int sum = 0;

    for (int i = 0; i < str.size(); i++)
    {

        if (m[str[i]] == 0)
        {

            m[str[i]]++;

            sum++;
        }
    }

    if (sum % 2 == 0)
    {

        printf("CHAT WITH HER!\n");
    }

    else
    {

        printf("IGNORE HIM!\n");
    }

    return 0;
}

K. 小亮的Team:
思路:每个题是否编写解决方案是独立的,只需看三个人的数字加起来是否大于1即可。

复制代码
#include <bits/stdc++.h>

using namespace std;

int main()
{

    int n;

    cin >> n;

    int sum = 0;

    for (int i = 0; i < n; i++)
    {

        int a, b, c;

        scanf("%d%d%d", &a, &b, &c);

        if (a + b + c >= 2)
        {

            sum++;
        }
    }

    printf("%d", sum);

    return 0;