• 2015年NOIP普及组复赛题解


    题目涉及算法:

    • 金币:入门题;
    • 扫雷游戏:入门题;
    • 求和:简单数学推导;
    • 推销员:贪心。

    金币

    题目链接:https://www.luogu.org/problem/P2669
    入门题,直接开一个循环遍历一下就可以了。
    实现代码如下:

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

    扫雷游戏

    题目链接:
    基础题,直接遍历一下地图(对于每个格子,统计一下周围8个格子)就可以实现地雷的统计。
    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 110;
    int n, m, c[maxn][maxn];
    char maze[maxn][maxn];
    int dir[8][2] = { -1, -1, -1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 0, 1, 1 };
    inline bool in_map(int x, int y) { return x >= 0 && x < n && y >= 0 && y < m; }
    int main() {
        cin >> n >> m;
        for (int i = 0; i < n; i ++) cin >> maze[i];
        for (int i = 0; i < n; i ++) for (int j = 0; j < m; j ++) {
            if (maze[i][j] == '*') for (int k = 0; k < 8; k ++) {
                int x = i + dir[k][0], y = j + dir[k][1];
                if (in_map(x, y)) c[x][y] ++;
            }
        }
        for (int i = 0; i < n; i ++) {
            for (int j = 0; j < m; j ++) {
                if (maze[i][j] == '*') putchar('*');
                else cout << c[i][j];
            }
            cout << endl;
        }
        return 0;
    }
    

    求和

    题目链接:https://www.luogu.org/problem/P2671
    这道题目是一道简单的数学推导。
    首先,因为一共有m种颜色,并且我们只有具有相同奇偶性的一对数才能凑成x和z,所以我们将所有从1到n的编号划分进 (2 imes m) 个集合,对于编号 (i)

    • 如果 (i) 是奇数,将其归到 (2 imes color[i]) 集合中;
    • 如果 (i) 是偶数,将其归到 (2 imes color[i] - 1) 集合中。

    然后我们去遍历每一个集合。
    如果该集合的元素个数是 (sz) ,并且我们设该集合中所有元素数值和 (S = sum number[i]) ,则我们遍历集合中的每个元素 (x) ,并将
    ((sz-1) imes x imes number[x] + x imes (S - number[x]))
    的结果加进我们的答案中。
    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 200020;
    const long long MOD = 10007;
    vector<int> vec[maxn];
    int n, m, color[maxn];
    long long ans, num[maxn], sum[maxn];
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; i ++) {
            cin >> num[i];
            num[i] %= MOD;
        }
        for (int i = 1; i <= n; i ++) cin >> color[i];
        for (int i = 1; i <= n; i ++) {
            int id = (i % 2) ? (color[i] * 2) : (color[i] * 2 - 1);
            vec[id].push_back(i);
            sum[id] = (sum[id] + num[i]) % MOD;
        }
        for (int i = 1; i <= 2*m; i ++) {
            int sz = vec[i].size();
            if (sz <= 1) continue;
            for (int j = 0; j < sz; j ++) {
                int v = vec[i][j];
                long long tmp = (long long)(sz-1) * (long long)v * (long long) num[v] % MOD;
                tmp += (long long) v * (sum[i] - num[v] + MOD) % MOD;
                ans = (ans + tmp) % MOD;
            }
        }
        cout << ans << endl;
        return 0;
    }
    

    推销员

    题目链接:https://www.luogu.org/problem/P2672
    这道题目是贪心,贪心的思想是:
    选择 (m) 户人家的最大疲劳值应该是以下两种方案中的较大值:

    • 方案一:选择 (a[i]) 最大的 (m) 户人家;
    • 方案二:选择 (a[i]) 最大的 (m-1) 户人家,以及剩下的 (n-(m-1)) 户人家中 (2 imes s[i] + a[i]) 最大的那户人家

    所以,我们可以给 (n) 户人家按照 (a[i]) 从大到小金星排序。
    然后在开三个数组(这3个数组用到了DP来进行求解):

    • suma[i] :表示 (sum_{j=1}^{i}a[j] + 2 imes max_{j=1}^{i}(s[j])) ,推导公式: suma[i] = suma[i-1] + a[i]
    • maxs[i] :表示 (max_{j=1}^{i} s[j]) ,推导公式: maxs[i] = maxs[i-1] + s[i]
    • maxsa[i] :表示 (max_{j=i}^{n} (2 imes s[j] + a[j])) ,推导公式:maxsa[i] = max(maxsa[i+1], 2 imes s[i] + a[i])

    然后我们要在 (n) 户人家里面选择 (m) 户人家访问的最大疲劳值就是 sum[m] + 2 * maxs[m]suma[m-1] + 2 * maxs[i] 的较大值。

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 100100;
    int n, s[maxn], a[maxn], idx[maxn], suma[maxn], maxs[maxn], maxsa[maxn];
    bool cmp(int i, int j) {
        return a[i] > a[j];
    }
    int main() {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> s[i];
        for (int i = 1; i <= n; i ++) cin >> a[i];
        for (int i = 1; i <= n; i ++) idx[i] = i;
        sort(idx+1, idx+1+n, cmp);
        for (int i = 1; i <= n; i ++)
            suma[i] = suma[i-1] + a[idx[i]], maxs[i] = max(maxs[i-1], s[idx[i]]);
        for (int i = n; i >= 1; i --)
            maxsa[i] = max(maxsa[i+1], s[idx[i]] * 2 + a[idx[i]]);
        for (int i = 1; i <= n; i ++)
            cout << max(suma[i]+2*maxs[i], suma[i-1]+maxsa[i]) << endl;
        return 0;
    }
    

    作者:zifeiy

  • 相关阅读:
    算法导论 第一章
    20155312 2016-2017-2 《Java程序设计》第七周学习总结
    Visual Studio 2005 搭建Windows CE 6.0环境之准备
    C#在winform中调用系统控制台输出
    C# 目录(文件夹)复制实现
    关于加强数据库安全的一些实践
    运维小白部署网站踩坑全过程
    jQuery学习之二 jQuery选择器
    运维系列之二 Linux文件种类和扩展名
    运维系列之一 Linux的文件与目录权限解析
  • 原文地址:https://www.cnblogs.com/codedecision/p/11750686.html
Copyright © 2020-2023  润新知