• 2003年NOIP普及组复赛题解


    题目涉及算法:

    • 乒乓球:简单字符串模拟;
    • 数字游戏:区间DP;
    • 栈:卡特兰数
    • 麦森数:高精度、快速幂、数学。

    乒乓球

    题目链接:https://www.luogu.org/problem/P1042
    这道题目是一道较为繁琐的字符串模拟,有点烦但是并不难。
    我的代码的思想是将到‘E’为止的输入全部存到一个字符串中,然后遍历字符串,用win_point来存放赢的分数,用lose_point来存放对手赢的分数,如果满足条件 (win_point>=11 || lose_point>=11) && abs(win_point-lose-point)>=2 ,则说明一局结束了(21分制同理)。
    这里需要注意的一点是:我最后还是需要输出当前这一句目前的比分(是‘0:0’也要输出)。
    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    char s[1000000], c;
    int n, win_point, lost_point;
    int main() {
        while ((c = getchar()) != 'E') {
            if (c == 'W' || c == 'L') s[n++] = c;
        }
        for (int i = 0; i < n; i ++) {
            s[i] == 'W' ? win_point ++ : lost_point ++;
            if ((win_point >= 11 || lost_point >= 11) && abs(win_point-lost_point) >= 2) {
                cout << win_point << ":" << lost_point << endl;
                win_point = lost_point = 0;
            }
        }
        cout << win_point << ":" << lost_point << endl;
        cout << endl;
        win_point = lost_point = 0;
        for (int i = 0; i < n; i ++) {
            s[i] == 'W' ? win_point ++ : lost_point ++;
            if ((win_point >= 21 || lost_point >= 21) && abs(win_point-lost_point) >= 2) {
                cout << win_point << ":" << lost_point << endl;
                win_point = lost_point = 0;
            }
        }
        cout << win_point << ":" << lost_point << endl;
        return 0;
    }
    

    数字游戏

    题目链接:https://www.luogu.org/problem/P1043
    这道题目涉及知识点:区间DP。
    这里面涉及一个环,但是我们可以拆开这个环。
    我们假设输入的是 (a_1 , dots , a_n) ,那么我们可以

    • (sum[i]) 表示 (a_1 + dots + a_i)
    • (maxv[i][j][k]) 表示区间 ([i,j]) 范围分成 (k) 份能够获得的最大乘积;
    • (maxv[i][j][k]) 表示区间 ([i,j]) 范围分成 (k) 份能够获得的最大乘积

    可以得到状态转移方程如下:
    (maxv[i][j][k] = max(maxv[i][j][k], maxv[i][l][k-1] * get_mod(sum[j]-sum[l]) ))
    (minv[i][j][k] = min(minv[i][j][k], minv[i][l][k-1] * get_mod(sum[j]-sum[l]) ))

    其中,(get_mod(a)) 用于获得 (a mod 10) 的结果。

    然后:
    最大值就是 (maxv[1][n][m]) 和所有满足条件的 (maxv[i][j][m-1] * get_mod(sum[i-1] + sum[n] - sum[j])) 中的最大值;
    最小值就是 (minv[1][n][m]) 和所有满足条件的 (minv[i][j][m-1] * get_mod(sum[i-1] + sum[n] - sum[j])) 中的最小值。
    这样就解决了环的问题,因为最外面的环肯定最多只占一段。
    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 55;
    int n, m, a[maxn], sum[maxn];
    long long maxv[maxn][maxn][10], minv[maxn][maxn][10], max_ans, min_ans;
    long long get_mod(long long a) {
        return (a % 10 + 10) % 10;
    }
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; i ++) {
            cin >> a[i];
            sum[i] = sum[i-1] + a[i];
        }
        if (m == 1) {
            cout << get_mod(sum[n]) << endl;
            cout << get_mod(sum[n]) << endl;
            return 0;
        }
        for (int k = 1; k <= m; k ++) {
            for (int i = 1; i+k-1 <= n; i ++) {
                for (int j = i+k-1; j <= n; j ++) {
                    if (k == 1) {
                        maxv[i][j][k] = get_mod(sum[j] - sum[i-1]);
                        minv[i][j][k] = get_mod(sum[j] - sum[i-1]);
                    }
                    else { // k > 1
                        for (int l = i+k-2; l < j; l ++) {
                            maxv[i][j][k] = max(maxv[i][j][k], maxv[i][l][k-1] * get_mod(sum[j]-sum[l]) );
                            minv[i][j][k] = min(minv[i][j][k], minv[i][l][k-1] * get_mod(sum[j]-sum[l]) );
                        }
                    }
                }
            }
        }
        max_ans = maxv[1][n][m];
        min_ans = minv[1][n][m];
        for (int i = 1; i <= n-m+1; i ++) {
            for (int j = i+m-2; j <= n; j ++) {
                max_ans = max(max_ans, maxv[i][j][m-1] * get_mod(sum[i-1] + sum[n] - sum[j]) );
                min_ans = min(max_ans, minv[i][j][m-1] * get_mod(sum[i-1] + sum[n] - sum[j]) );
            }
        }
        cout << min_ans << endl;
        cout << max_ans << endl;
        return 0;
    }
    

    题目描述:https://www.luogu.org/problem/P1044
    这道题目就是一道“卡特兰数”,我时直接套公式做的(当然也有递推公式),公式如下:
    (Ci = frac1{n+1}C_{2n}^n)
    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    int n;
    long long m1 = 1, m2 = 1, tmp;
    int main() {
        cin >> n;
        for (int i = n; i >= 1; i --) {
            m1 *= i+n;
            m2 *= i;
            tmp = __gcd(m1, m2);
            m1 /= tmp;
            m2 /= tmp;
        }
        m1 /= n+1;
        cout << m1 << endl;
        return 0;
    }
    

    麦森数

    题目链接:https://www.luogu.org/problem/P1045
    这道题目一眼看过去就是高精度+快速幂。
    这里有一个优化,就是长度,因为是2进制的P位数,并且它在十进制下不会是连续的全都是 (9) ,所以 (2^P-1) 肯定和 (2^P) 具有相同的位数。
    所以长度可以直接通过公式 (lceil P imes frac{log 2}{log 10} ceil) 获得。
    然后最暴力的快速幂高精度幂是会超时的,但是如果我们在运算过程中保证因数的长补不超过500(截取末尾500位),就不会超时。
    (然后我因为年纪比较大了,所以高精度运算直接搬了我之前写的模板囧~ 所以大家还是只看主函数就好了, multi 函数就是高精度乘, Sub函数就是高精度减)
    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 10000010;
    
    int a[maxn], b[maxn], res[maxn];
    
    string add(string s1, string s2) {  // under condition: s1,s2>=0
        // 初始化部分
        int n = s1.length(), m = s2.length();
        for (int i = 0; i < n; i ++) a[i] = s1[n-1-i] - '0';
        for (int i = 0; i < m; i ++) b[i] = s2[m-1-i] - '0';
        int len = max(n, m) + 1;
        for (int i = n; i < len; i ++) a[i] = 0;
        for (int i = m; i < len; i ++) b[i] = 0;
        for (int i = 0; i < len; i ++) res[i] = 0;
        // 处理部分
        for (int i = 0; i < len; i ++) {
            res[i] += a[i] + b[i];
            if (res[i] >= 10) {
                res[i+1] += res[i] / 10;
                res[i] %= 10;
            }
        }
        // 返回部分
        int i = len-1;
        while (res[i] == 0 && i > 0) i --;
        string s = "";
        for (; i >= 0; i --) {
            char c = (char) (res[i] + '0');
            s += c;
        }
        return s;
    }
    
    string sub(string s1, string s2) {  // under condition: s1>=s2>=0
        // 初始化部分
        int n = s1.length(), m = s2.length();
        for (int i = 0; i < n; i ++) a[i] = s1[n-1-i] - '0';
        for (int i = 0; i < m; i ++) b[i] = s2[m-1-i] - '0';
        int len = max(n, m);
        for (int i = n; i < len; i ++) a[i] = 0;
        for (int i = m; i < len; i ++) b[i] = 0;
        for (int i = 0; i < len; i ++) res[i] = 0;
        // 处理部分
        for (int i = 0; i < len; i ++) {
            res[i] += a[i] - b[i];
            if (res[i] < 0) {
                res[i+1] --;
                res[i] += 10;
            }
        }
        // 返回部分
        int i = len-1;
        while (res[i] == 0 && i > 0) i --;
        string s = "";
        for (; i >= 0; i --) {
            char c = (char) (res[i] + '0');
            s += c;
        }
        return s;
    }
    
    bool cmp(string s1, string s2) {    // under condition: s1,s2 >= 0
        int n = s1.length(), m = s2.length();
        int i;
        for (i = 0; i < n-1 && s1[i] == '0'; i ++);
        s1 = s1.substr(i);
        for (i = 0; i < m-1 && s2[i] == '0'; i ++);
        s2 = s2.substr(i);
        if (s1.length() != s2.length()) return s1.length() < s2.length();
        return s1 < s2;
    }
    
    string Add(string s1, string s2) {
        if (s1[0] == '-' && s2[0] == '-') {
            return "-" + add(s1.substr(1), s2.substr(1));
        }
        else if (s1[0] == '-') {
            s1 = s1.substr(1);
            if (cmp(s1, s2) == true) {
                return sub(s2, s1);
            } else {
                return "-" + sub(s1, s2);
            }
        }
        else if (s2[0] == '-') {
            s2 = s2.substr(1);
            if (cmp(s1, s2) == true) {
                return "-" + sub(s2, s1);
            } else {
                return sub(s1, s2);
            }
        }
        else {
            return add(s1, s2);
        }
    }
    
    string Sub(string s1, string s2) {
        if (s2[0] == '-') {
            s2 = s2.substr(1);
            return Add(s1, s2);
        }
        else {
            return Add(s1, "-" + s2);
        }
    }
    
    string multi(string s1, string s2) {    // under condition: s1,s2>=0
        // 初始化部分
        int n = s1.length(), m = s2.length();
        for (int i = 0; i < n; i ++) a[i] = s1[n-1-i] - '0';
        for (int i = 0; i < m; i ++) b[i] = s2[m-1-i] - '0';
        int len = n + m;
        for (int i = n; i < len; i ++) a[i] = 0;
        for (int i = m; i < len; i ++) b[i] = 0;
        for (int i = 0; i < len; i ++) res[i] = 0;
        // 处理部分
        for (int i = 0; i < n; i ++)
            for (int j = 0; j < m; j ++)
                res[i+j] += a[i] * b[j];
        for (int i = 0; i < len; i ++) {
            res[i+1] += res[i] / 10;
            res[i] %= 10;
        }
        // 返回部分
        int i = len-1;
        while (res[i] == 0 && i > 0) i --;
        string s = "";
        for (; i >= 0; i --) {
            char c = (char) (res[i] + '0');
            s += c;
        }
        return s;
    }
    
    pair<string, string> divide(string s1, string s2) { // under condition: s1>=0,s2>0
        string s = "", t = "";
        int n = s1.length(), m = s2.length();
        bool flag = false;
        for (int i = 0; i < n; i ++) {
            s += s1[i];
            int num = 0;
            while (cmp(s, s2) == false) {
                num ++;
                s = sub(s, s2);
            }
            if (num > 0) {
                flag = true;
                char c = (char)(num + '0');
                t += c;
            }
            else if (flag) {
                t += '0';
            }
        }
        if (t.length() == 0) t = "0";
        while (s[0] == '0' && s.length() > 1) s = s.substr(1);
        return make_pair(t, s);
    }
    
    string s = "2", t = "1", ans = "";
    int P;
    
    int main() {
        cin >> P;
        int ans_len = ceil(P * log(2) / log(10));
        cout << ans_len << endl;
        while (P > 1) {
            if (P % 2) {
                t = multi(t, s);
                if (t.length() > 500) t = t.substr(t.length()-500, 500);
            }
            s = multi(s, s);
            if (s.length() > 500) s = s.substr(s.length()-500, 500);
            P /= 2;
        }
        s = multi(s, t);
        s = Sub(s, "1");
        int len = s.length();
        if (len <= 500) for (int i = 0; i < 500 - len; i ++) ans += "0";
        if (len <= 500)
            ans += s;
        else
            ans = s.substr(len-500, 500);
        for (int i = 0; i < 10; i ++) cout << ans.substr(i*50, 50) << endl;
        return 0;
    }
    
  • 相关阅读:
    团队冲刺二(6)
    团队冲刺二(5)
    JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结
    解决ADB server didn't ACK问题,连上手机问题
    ADB server didn't ACK
    android错误
    Andy
    在Eclipse下搭建Android开发环境教程
    VM VirtualBox 安装 Android 4.3虚拟机完整教程
    电脑安装Android4.0虚拟机的做法
  • 原文地址:https://www.cnblogs.com/codedecision/p/11714911.html
Copyright © 2020-2023  润新知