2009年NOIP普及组复赛题解

题目涉及算法:

  • 多项式输出:模拟;
  • 分数线划定:模拟、排序;
  • 细胞分裂:质因数分解;
  • 道路游戏:动态规划。

多项式输出

题目链接:https://www.luogu.org/problem/P1067
纯模拟题。注意符号和1、0。
实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
int n, a[maxn];
int main() {
    cin >> n;
    for (int i = n; i >= 0; i --) cin >> a[i];
    while (n > 0 && !a[n]) n --;
    if (n == 0) {
        cout << a[0] << endl;
        return 0;
    }
    for (int i = n; i >= 0; i --) {
        if (!a[i]) continue;
        if (i < n && a[i] > 0) putchar('+');
        if (i == 0) {
            cout << a[i];
            continue;
        }
        if (a[i] == 1) ;
        else if (a[i] == -1) putchar('-');
        else cout << a[i];
        if (i > 0) cout << "x";
        if (i > 1) cout << "^" << i;
    }
    cout << endl;
    return 0;
}

分数线划定

题目链接:https://www.luogu.org/problem/P1068
这道题是一道简单的模拟题,只需要排序+输出响应结果即可。
实现代码如下:

#include <bits/stdc++.h>
using namespace std;
struct Node {
    int k, s;  // 分别表示报名号和成绩
} a[5005];
int n, m;
bool cmp(Node a, Node b) {
    if (a.s != b.s) // 如果成绩不同
        return a.s > b.s;   // 按成绩从高到低排
    return a.k <b.k; // 否则,按报名号从小到大排
}
int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i ++)
        cin >> a[i].k >> a[i].s;
    sort(a, a+n, cmp);
    int line = a[m*3/2-1].s;    // line表示分数线
    int id = m*3/2-1;   // id对应最后录取的那个人的坐标
    while (id+1 < n && a[id+1].s == line) id ++;
    cout << line << " " << id+1 << endl; // 输出分数线及录取人数
    for (int i = 0; i <= id; i ++)  // 输出录取人的名单
        cout << a[i].k << " " << a[i].s << endl;
    return 0;
}

细胞分裂

题目链接:https://www.luogu.org/problem/P1069
这道题目考的其实是质因数分解。
这道题目可以翻译成这样:
给你一个数 \(M = m1^{m2}\) ,然后给你 \(n\) 个数 \(S_i\) ,在所有 \(Si\) 中你要找到一个最小的答案 \(ans\) 使得 \(S_i^{ans}\) 能被 \(M\) 整数。
使用质因数分解直接解决。
实现代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, m1, m2, cnt, s[maxn], a[maxn], b[maxn], c[maxn], ans = -1;
int main() {
    cin >> n >> m1 >> m2;
    int m = m1;
    for (int i = 2; i * i <= m1; i ++) {
        if (m % i == 0) {
            a[cnt] = i;
            while (m % i == 0) {
                m /= i;
                c[cnt] ++;
            }
            cnt ++;
        }
    }
    if (m > 1) {
        a[cnt] = m;
        c[cnt++] = 1;
    }
    for (int i = 0; i < cnt; i ++) c[i] *= m2;
    for (int i = 0; i < n; i ++) {
        cin >> s[i];
        m = s[i];
        memset(b, 0, sizeof(int)*cnt);
        bool flag = true;
        for (int j = 0; j < cnt; j ++) {
            if (m % a[j]) {
                flag = false;
                break;
            }
            while (m % a[j] == 0) {
                b[j] ++;
                m /= a[j];
            }
        }
        if (flag) {
            int tmp = 0;
            for (int j = 0; j < cnt; j ++) {
                tmp = max(tmp, (c[j] + b[j]-1) / b[j] );
            }
            if (ans == -1 || ans > tmp) ans = tmp;
        }
    }
    cout << ans << endl;
    return 0;
}

道路游戏

题目链接:https://www.luogu.org/problem/P1070

这道题目使用动态规划解决。
我们假设有n个点,m个时刻,则我们令:

  • \(cost[i]\) :表示第 \(i\) 个点部署一个机器人的花费;
  • \(coin[i][j]\) :表示第 \(j\) 时刻第 \(i\) 条路的金币;
  • \(f[i][j]\) :表示第 \(j\) 时刻处在第 \(i\) 个点的最高收益。

我们可以通过当前的 \(f[i][j]\) 来更新 \(f[i+1][j+1]、f[i+2][j+2]、\dots f[i+p][j+p]\) ,因为我们可以假设从 \(f[i][j]\) 花费 \(cost[i]\) 来部署一个机器人来更新后面的 \(p\) 个状态。

同时,\(f[i][j]\) 应该是线面两项中的最大值:

  1. \(f[i][j]\) 本身,这个状态是根据之前 \(\lt i\) 的若干状态推导出来的;
  2. \(\max ( f[k][j] )\) ,其中 \(1 \le k \le n\)

因为我可以从任何一个 \(f[k][j]\) 跳转到第 \(i\) 个点来部署机器人。

这道题目一定要注意好多题目描述中的细节。

之前一直错最后一组数据,后来次啊发现是因为题目描述中有一句话:
“当马路上的机器人行走完规定的次数之后会自动消失,小新必须立刻在任意一个机器人工厂中购买一个新的机器人,并给新的机器人设定新的行走次数。”
这就是说:我每时每刻都得布置机器人,所以需要将 \(f[i][j]\) 初始值置为 \(- \infty\)
下面是修改后AC的代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int n, m, p;
long long coin[maxn][maxn], cost[maxn], f[maxn][maxn], ans;
void init() {
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= m+1; j ++) {
            if (j == 1) f[i][j] = 0;
            else f[i][j] = INT_MIN;
        }
    }
}
int main() {
    cin >> n >> m >> p;
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
            cin >> coin[i][j];
    for (int i = 1; i <= n; i ++) cin >> cost[i];
    init();
    for (int j = 1; j <= m; j ++) {
        long long maxf = INT_MIN;
        for (int i = 1; i <= n; i ++) maxf = max(maxf, f[i][j]);
        for (int i = 1; i <= n; i ++) {
            f[i][j] = maxf;
            long long tmp = f[i][j] - cost[i];
            int ii = i, jj = j;
            for (int k = 0; k < p; k ++) {
                tmp += coin[ii][jj];
                ii ++;
                jj ++;
                if (ii > n) ii = 1;
                if (jj > m+1) break;
                f[ii][jj] = max(f[ii][jj], tmp);
            }
        }
    }
    for (int i = 1; i <= n; i ++)
        ans = max(ans, f[i][m+1]);
    cout << ans << endl;
    return 0;
}

作者:zifeiy

posted @ 2019-10-22 20:51  codedecision  阅读(902)  评论(0编辑  收藏  举报