NOIP 2001 普及组算法题解

作者: | 更新日期:

涉及动态规划、前中后序转换、01背包等算法

本文首发于公众号:天空的代码世界,微信号:tiankonguse

零、背景

今天继续分享 2001 年 NOIP 普及组题解。

截图

一、数的计算

题意:给出数列的第一个数字,按规则构造正整数数列,问可以构造多少个不同的数列。
构造规则:相邻两个数字,下个数字不能超过上个数字的一半。
数据范围:1000

思路:动态规划

状态定义:
dp[i] 以 i 为第一个数字的不同数列个数。

状态转移方程:

dp[0] = 1;
// j <= i/2
dp[i] = sum(dp[j]);  

复杂度:O(n^2)

优化:预处理状态前缀和。

预处理状态:
sum[i] 以小于等于 i 为第一个数字的不同数列个数。

dp[0] = 1;
sum[0] = 1;

dp[i] = sum[i/2];
sum[i] = sum[i-1] + dp[i];

复杂度:O(n)

二、最大公约数和最小公倍数问题

题意:输入两个正整数 X 和 Y,问存在多少个正整数二元组 (P,Q) 满足 X 是最大公约数,Y 是最小公倍数。

思路:枚举

由于 X 是 (P,Q) 的最大公约数,可以推导出 gcd(P,Q) = X
进而推导出 gcd(P/X, Q/X) = 1

PX * X = PQX * X = Q,则可以推导出
最小公倍数的关系 Y = PX * QX * X

现在已知 YX,需要求 PXQX
且需要满足 Y % X == 0gcd(PX, QX) = 1

枚举 PX,求 QX 即可。

ll ans = 0;
if (y % x == 0) {
  ll YX = y / x;
  for (ll PX = 1; PX <= YX; PX++) {
    if (YX % PX == 0) {
      ll PY = YX / PX;
      if (Gcd(PX, PY) != 1) continue;
      ans++;
    }
  }
}

三、求先序排列

题意:给出中序和后序序列,求先序序列。

思路:递归

先预处理每个值对应的位置,然后递归处理即可。

Dfs(0, n - 1, 0, n - 1, 0, n - 1);
void Dfs(int l1, int r1, int l2, int r2, int l3, int r3) {
  if (l1 > r1) return;
  S3[l3] = S2[r2];
  int mid = mp1[S2[r2]];
  int leftLen = mid - l1;
  Dfs(l1, mid - 1, l2, l2 + leftLen - 1, l3 + 1, l3 + leftLen);
  Dfs(mid + 1, r1, l2 + leftLen, r2 - 1, l3 + leftLen + 1, r3);
}

四、装箱问题

题意:给一个容量为 V 的箱子,以及 n 个有自己体积的物品,问箱子的剩余空间最小是多少。

思路:01 背包

// 01背包
int V, n;
vector<int> vs;
vector<int> DP;
void Solver() {  //
  scanf("%d%d", &V, &n);
  vs.resize(n);
  DP.resize(V + 1, 0);
  for (int i = 0; i < n; i++) {
    scanf("%d", &vs[i]);
  }
  for (int i = 0; i < n; i++) {
    for (int j = V; j >= vs[i]; j--) {
      DP[j] = max(DP[j], DP[j - vs[i]] + vs[i]);
    }
  }
    printf("%d\n", V - DP[V]);
}

五、最后

这次比赛的四道题都不难。
第一题,动态规划。
第二题,数学计算与枚举。
第三题,递归。
第四题,动态规划 01 背包。

《完》

-EOF-

本文公众号:天空的代码世界
个人微信号:tiankonguse
公众号 ID:tiankonguse-code

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越