📌 点击直达笔试专栏 👉《大厂笔试突围》
💻 春秋招笔试突围在线OJ 👉 笔试突围OJ
02. K小姐的魔法花园
问题描述
K 小姐是一位热爱园艺的魔法师。她有一个神奇的花园,里面种植着 n n n 朵魔法花。每朵花都有一个魔力值,初始时第 i i i 朵花的魔力值为 A [ i ] A[i] A[i]。K 小姐想要让花园的总魔力值成为一个特定数字 x x x 的倍数。
为了达成目标,K 小姐可以进行两种魔法操作:
- 将一朵花从花园中移除。
- 对一朵花施加增强魔法,使其魔力值增加 1。
K 小姐想知道,最少需要进行多少次魔法操作,才能使花园中剩余的花的魔力值之和成为 x x x 的倍数(如果移除所有的花,总和视为 0,也是 x x x 的倍数)。
输入格式
第一行包含两个正整数 n n n 和 x x x,分别表示魔法花的数量和目标倍数。
第二行包含 n n n 个正整数 A 1 , A 2 , ... , A n A_1, A_2, \ldots, A_n A1,A2,...,An,表示每朵魔法花的初始魔力值。
输出格式
输出一个整数,表示 K 小姐需要进行的最少魔法操作次数。
样例输入
1 3
4
3 5
2 7 6
样例输出
1
2
样例 | 解释说明 |
---|---|
样例1 | 直接将唯一的魔法花移除,此时花园的总魔力值为0,是3的倍数,操作次数为1 |
样例2 | 移除第2朵花(魔力值7),剩余花的总魔力值为2+6=8,需要增强2次变为10(5的倍数),总操作次数为1+2=3。或者移除第1、3朵花,总操作次数为2 |
数据范围
- 1 ≤ n ≤ 1000 1 \leq n \leq 1000 1≤n≤1000
- 1 ≤ x ≤ 1000 1 \leq x \leq 1000 1≤x≤1000
- 1 ≤ A i ≤ 1000 1 \leq A_i \leq 1000 1≤Ai≤1000
题解
这是一个经典的同余动态规划问题。对于每朵花,都有两种选择:移除或保留。
关键观察:我们只关心魔力值之和模 x x x 的余数。设当前剩余魔力值之和为 S S S,那么需要增强 ( x − S m o d x ) m o d x (x - S \bmod x) \bmod x (x−Smodx)modx 次才能使总和成为 x x x 的倍数。
使用记忆化搜索解决:
- 状态定义: dfs ( i , mod ) \text{dfs}(i, \text{mod}) dfs(i,mod) 表示处理完前 i i i 朵花,当前魔力值之和模 x x x 等于 mod \text{mod} mod 时的最小操作次数
- 状态转移:对于第 i i i 朵花,可以选择移除(操作次数+1)或保留
- 边界条件:当处理完所有花时,返回需要增强的次数
时间复杂度为 O ( n × x ) O(n \times x) O(n×x),空间复杂度为 O ( n × x ) O(n \times x) O(n×x)。
参考代码
- Python
python
import sys
input = lambda: sys.stdin.readline().strip()
def solve():
# 读取输入
n, x = map(int, input().split())
nums = list(map(int, input().split()))
# 计算初始总和的余数
total = sum(nums) % x
# 记忆化数组
memo = {}
def dp(pos, mod):
# 如果处理完所有花
if pos == n:
return (x - mod) % x
# 如果已经计算过
if (pos, mod) in memo:
return memo[(pos, mod)]
# 选择移除当前花
res = dp(pos + 1, mod) + 1
# 选择保留当前花
new_mod = (mod + nums[pos]) % x
res = min(res, dp(pos + 1, new_mod))
memo[(pos, mod)] = res
return res
print(dp(0, 0))
solve()
- Cpp
cpp
#include
#include
#include
using namespace std;
int n, x;
vector nums;
int memo[1005][1005];
// 递归求解最小操作数
int dp(int pos, int mod) {
// 处理完所有花朵
if (pos == n) {
return (x - mod) % x;
}
// 已经计算过的状态
if (memo[pos][mod] != -1) {
return memo[pos][mod];
}
// 移除当前花朵
int res = dp(pos + 1, mod) + 1;
// 保留当前花朵
int new_mod = (mod + nums[pos]) % x;
res = min(res, dp(pos + 1, new_mod));
return memo[pos][mod] = res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> x;
nums.resize(n);
int total = 0;
for (int i = 0; i < n; i++) {
cin >> nums[i];
total = (total + nums[i]) % x;
}
// 初始化记忆化数组
memset(memo, -1, sizeof(memo));
cout << dp(0, 0) << "\n";
return 0;
}
- Java
java
import java.util.Scanner;
public class Main {
static int n, x;
static int[] nums;
static int[][] memo;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取输入数据
n = sc.nextInt();
x = sc.nextInt();
nums = new int[n];
int total = 0;
for (int i = 0; i < n; i++) {
nums[i] = sc.nextInt();
total = (total + nums[i]) % x;
}
// 初始化记忆化数组
memo = new int[n][x];
for (int i = 0; i < n; i++) {
for (int j = 0; j < x; j++) {
memo[i][j] = -1;
}
}
System.out.println(solve(0, 0));
sc.close();
}
// 动态规划求解
static int solve(int pos, int mod) {
// 处理完所有花朵
if (pos == n) {
return (x - mod) % x;
}
// 记忆化查询
if (memo[pos][mod] != -1) {
return memo[pos][mod];
}
// 移除花朵
int result = solve(pos + 1, mod) + 1;
// 保留花朵
int newMod = (mod + nums[pos]) % x;
result = Math.min(result, solve(pos + 1, newMod));
return memo[pos][mod] = result;
}
}