Back

NOIP2010普及组题解

数字统计

统计[L, R]的所有整数中,数字2出现的次数。

题解

经典题,这题数据范围较小,直接暴力。

#include <bits/stdc++.h>
using namespace std;
int main() {
  int count = 0, l, r;
  cin >> l >> r;
  for (int i = l; i <= r; i++) {
    int x = i;
    while(x > 0) {
      count += (x%10 == 2);
      x /= 10;
    }
  }
  cout << count << endl;
}

接水问题

n个学生,m个水龙头按顺序接水,计算总时间。

方法1

模拟,时间复杂度$O(nm)$。

#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
int n, m, a[N], b[N];
int main() {
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
  }
  int ans = 0;
  for (int i = 1; i <= n; i++) {
    int mn = 1e9, k;
    for (int j = 1; j <= m; j++) {
      if (mn > b[j]) {
        mn = b[j];
        k = j;
      }
    }
    if (mn == 0) {
      b[k] = a[i];
    } else {
      ans += mn;
      for (int j = 1; j <= m; j++) {
        b[j] -= mn;
      }
      b[k] = a[i];
    }
  }
  int mx = 0;
  for (int j = 1; j <= m; j++) {
    mx = max(mx, b[j]);
  }
  ans += mx;
  cout << ans << endl;
}

方法2

也可以用优先队列,每次pop最小的出队时间,push当前同学下次出队的时间,时间复杂度到$O(n\log{m})$。

#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
int n, m, a[N], b[N];
int main() {
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
  }
  priority_queue<int, vector<int>, greater<int> > q;
  int cur = 0;
  for (int i = 1; i <= n; i++) {
    if (q.size() == m) {
      cur = q.top();
      q.pop();
    }
    q.push(cur + a[i]);
  }
  while(!q.empty()) {
    cur = q.top();
    q.pop();
  }
  cout << cur << endl;
}

导弹拦截

给两个圆心坐标和$n$个点,求最小的半径平方和覆盖所有的点。

题解

对每个点分别计算出到两个圆心的距离的平方,记为$a_i$,$b_i$。 最优解一定存在一个点在圆上,考虑枚举在第一个圆上的点$i$,比$a_i$小的可以被覆盖到, 比$a_i$大的需要用第二个圆覆盖,所以需要计算剩下的点中$b$的最大值。
对$(a_i, b_i)$按照$a_i$排序,那么剩下的点就是$i+1$到$n$之间的点, 这段区间$b$的最大值可用类似前缀和的方法计算出来。

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, b[N];
pair<int, int> a[N];
int main() {
  int x1, y1, x2, y2;
  cin >> x1 >> y1 >> x2 >> y2;
  cin >> n;
  for (int i = 1; i <= n; i++) {
    int x, y;
    scanf("%d%d", &x, &y);
    a[i].first = (x-x1) * (x-x1) + (y-y1)*(y-y1);
    a[i].second = (x-x2) * (x-x2) + (y-y2)*(y-y2);
  }
  sort(a + 1, a + n + 1);
  for (int i = n; i >= 1; i--) {
    b[i] = max(b[i+1], a[i].second);
  }
  int ans = 1e9;
  for (int i = 1; i <= n + 1; i++) {
    ans = min(ans, a[i-1].first + b[i]);
  }
  cout << ans << endl;
}

三国游戏

给一个对称的$n*n$的矩阵P,n为偶数。A同学和B同学交替从$1$到$n$选数字。在所有数字选完之后,A和B分别从自己选择的$n/2$个数字中选出两个$(i,j)$,使得$P_{i,j}$最大,数字大的一方获胜。B的策略为不让A选择到当前可选的最大的组合,问A先手能否获胜,能选到的最大的$P$为多少。

题解

在B的策略下,A和B都选不到每个数的最大匹配,所以A只能选出每个数的第二大的匹配,那么所有数的第二大匹配的最大值就是A能选出来的最大值。

#include <bits/stdc++.h>
using namespace std;
const int N = 505;
int a[N][N], n;
int main() {
  cin >> n;
  for (int i = 1 ; i <= n; i++) {
    for (int j = i + 1; j <= n; j++) {
      scanf("%d", &a[i][j]);
      a[j][i] = a[i][j];
    }
  }
  int ans = 0;
  for (int i = 1; i <= n; i++) {
    sort(a[i]+1, a[i]+n+1);
    ans = max(ans, a[i][n-1]);
  }
  cout << 1 << endl << ans << endl;
}