题目链接

矩形游戏

题目描述

颗石子开始,每次操作可以选择将当前的 颗石子摆成 的矩形(其中 ),然后保留其中一行的 颗石子,其余丢弃。游戏在剩下 1 颗石子时结束。游戏得分是每轮操作前所拥有的石子数之和。求最优策略下的最大得分。

解题思路

本题要求一个最大得分,这是一个典型的最优化问题,我们可以尝试用动态规划的思路来分析。

表示从 颗石子开始游戏能获得的最大得分。 根据题意,游戏在剩下 1 颗石子时结束,所以 。 对于 的情况,一次操作将石子数从 变为 ,其中 的一个真因子()。得分会增加 ,之后的游戏得分则取决于从 开始能获得的最大分数 。 为了使总分最大,我们应该选择一个能使后续得分 最大的真因子 。因此,我们可以写出状态转移方程:

接下来我们分析 函数的性质。 是一个严格单调递增的函数。也就是说,如果 ,那么 。 这是因为 至少等于 ,所以 保证了 的首项就大于 的首项,后续的累加项也遵循这个规律,因此总和也更大。

既然 随着 的增大而增大,那么为了让 最大,我们应该选择最大的那个真因子 。 一个数 的最大真因子可以通过用 除以它的最小质因子得到。例如, 的最小质因子是 ,其最大真因子就是 的最小质因子是 ,其最大真因子是

因此,最优策略在每一步都是确定的:从当前的石子数 变成它的最大真因子

算法流程如下:

  1. 初始化总得分 score 为 0,当前石子数 current_n
  2. 进入一个循环,只要 current_n 大于 0: a. 将 current_n 加入到 score 中。 b. 如果 current_n 等于 1,游戏结束,跳出循环。 c. 找到 current_n 的最小质因子 。 d. 更新 current_n 为它的最大真因子,即 current_n / p
  3. 返回 score

寻找最小质因子可以通过试除法实现:从 2 开始向上遍历,第一个能整除 current_n 的数就是它的最小质因子。

代码

#include <iostream>

using namespace std;

// 查找n的最小质因子
long long find_smallest_prime_factor(long long n) {
    if (n % 2 == 0) {
        return 2;
    }
    for (long long i = 3; i * i <= n; i += 2) {
        if (n % i == 0) {
            return i;
        }
    }
    // 如果没有找到小于等于其平方根的因子,说明n本身是质数
    return n;
}

int main() {
    long long n;
    cin >> n;
    long long score = 0;
    while (n > 0) {
        score += n;
        if (n == 1) {
            break;
        }
        long long p = find_smallest_prime_factor(n);
        n /= p;
    }
    cout << score << "\n";
    return 0;
}
import java.util.Scanner;

public class Main {
    // 查找n的最小质因子
    public static long findSmallestPrimeFactor(long n) {
        if (n % 2 == 0) {
            return 2;
        }
        for (long i = 3; i * i <= n; i += 2) {
            if (n % i == 0) {
                return i;
            }
        }
        // 如果没有找到小于等于其平方根的因子,说明n本身是质数
        return n;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong();
        long score = 0;
        
        while (n > 0) {
            score += n;
            if (n == 1) {
                break;
            }
            long p = findSmallestPrimeFactor(n);
            n /= p;
        }
        System.out.println(score);
    }
}
def find_smallest_prime_factor(n):
    """查找n的最小质因子"""
    if n % 2 == 0:
        return 2
    i = 3
    while i * i <= n:
        if n % i == 0:
            return i
        i += 2
    # 如果没有找到小于等于其平方根的因子,说明n本身是质数
    return n

n = int(input())
score = 0
while n > 0:
    score += n
    if n == 1:
        break
    p = find_smallest_prime_factor(n)
    n //= p

print(score)

算法及复杂度

  • 算法:贪心算法 + 试除法
  • 时间复杂度:。游戏进行的步数是 的质因子个数,为 级别。每一步都需要寻找最小质因子,这需要 的时间,其中 是当前步的石子数。整个过程的总时间由第一步寻找最小质因子的时间决定,即
  • 空间复杂度:,只需要常数个变量。