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 \times m\) 个集合,对于编号 \(i\)

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

然后我们去遍历每一个集合。
如果该集合的元素个数是 \(sz\) ,并且我们设该集合中所有元素数值和 \(S = \sum number[i]\) ,则我们遍历集合中的每个元素 \(x\) ,并将
\((sz-1) \times x \times number[x] + x \times (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;
}

推销员

题目链接:
本题涉及算法:贪心。
贪心思想:访问 \(n\) 户人家的最大值应该是如下两者的较大值:

  • 访问 \(a_i\) 最大的前 \(n\) 户人家;
  • 访问 \(a_i\) 最大的前 \(n-1\) 户人家 以及 \(s_i\) 最大的那户人家。

基于这种思想,实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n;
struct Node {
    long long a, s;
} a[maxn];
long long sum[maxn];
bool cmp(Node a, Node b) {
    return a.a > b.a || a.a == b.a && a.s > b.s;
}
int main() {
    cin >> n;
    for (int i = 0; i < n; i ++) cin >> a[i].s;
    for (int i = 0; i < n; i ++) cin >> a[i].a;
    sort(a, a+n, cmp);
    int id = 0;
    for (int i = 1; i < n; i ++) if (a[i].s > a[id].s) id = i;
    sum[0] = a[0].a;
    for (int i = 1; i < n; i ++) sum[i] = sum[i-1] + a[i].a;
    long long maxs = a[0].s;
    // 0 的1情况
    long long res = max(a[id].s * 2 + a[id].a, a[0].s * 2 + a[0].a);
    cout << res << endl;
    for (int i = 1; i < n; i ++) {
        maxs = max(maxs, a[i].s);
        if (i >= id) res = sum[i] + 2 * maxs;
        else res = max(sum[i] + 2 * maxs, sum[i-1] + 2 * a[id].s + a[id].a);
        cout << res << endl;
    }
    return 0;
}

作者:zifeiy

猜你喜欢

转载自www.cnblogs.com/codedecision/p/11750686.html